xref: /netbsd-src/external/bsd/ntp/dist/ntpq/ntpq.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: ntpq.c,v 1.23 2024/08/18 20:47:19 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel /*
4abb0f93cSkardel  * ntpq - query an NTP server using mode 6 commands
5abb0f93cSkardel  */
62950cc38Schristos #include <config.h>
7abb0f93cSkardel #include <ctype.h>
8abb0f93cSkardel #include <signal.h>
9abb0f93cSkardel #include <setjmp.h>
104eea345dSchristos #include <stddef.h>
114eea345dSchristos #include <stdio.h>
12abb0f93cSkardel #include <sys/types.h>
13abb0f93cSkardel #include <sys/time.h>
142950cc38Schristos #ifdef HAVE_UNISTD_H
152950cc38Schristos # include <unistd.h>
162950cc38Schristos #endif
172950cc38Schristos #ifdef HAVE_FCNTL_H
182950cc38Schristos # include <fcntl.h>
192950cc38Schristos #endif
202950cc38Schristos #ifdef SYS_WINNT
212950cc38Schristos # include <mswsock.h>
22*eabc0478Schristos # define PATH_DEVNULL	"NUL:"
23*eabc0478Schristos #else
24*eabc0478Schristos # define PATH_DEVNULL	"/dev/null"
252950cc38Schristos #endif
262950cc38Schristos #include <isc/net.h>
272950cc38Schristos #include <isc/result.h>
28abb0f93cSkardel 
29abb0f93cSkardel #include "ntpq.h"
30abb0f93cSkardel #include "ntp_unixtime.h"
31abb0f93cSkardel #include "ntp_calendar.h"
32abb0f93cSkardel #include "ntp_select.h"
33abb0f93cSkardel #include "ntp_lineedit.h"
34abb0f93cSkardel #include "ntp_debug.h"
352950cc38Schristos #ifdef OPENSSL
362950cc38Schristos # include "openssl/evp.h"
372950cc38Schristos # include "openssl/objects.h"
385d681e99Schristos # include "openssl/err.h"
394eea345dSchristos # ifdef SYS_WINNT
404eea345dSchristos #  include "openssl/opensslv.h"
414eea345dSchristos #  if !defined(HAVE_EVP_MD_DO_ALL_SORTED) && OPENSSL_VERSION_NUMBER > 0x10000000L
424eea345dSchristos #     define HAVE_EVP_MD_DO_ALL_SORTED	1
434eea345dSchristos #  endif
444eea345dSchristos # endif
4503cfe0ffSchristos # include "libssl_compat.h"
4679045f13Schristos # ifdef HAVE_OPENSSL_CMAC_H
4779045f13Schristos #  include <openssl/cmac.h>
484eea345dSchristos #  define CMAC "AES128CMAC"
492950cc38Schristos # endif
5079045f13Schristos #endif
51abb0f93cSkardel #include <ssl_applink.c>
52abb0f93cSkardel 
533123f114Skardel #include "ntp_libopts.h"
548b8da087Schristos #include "safecast.h"
55abb0f93cSkardel 
562950cc38Schristos #ifdef SYS_VXWORKS		/* vxWorks needs mode flag -casey*/
57abb0f93cSkardel # define open(name, flags)   open(name, flags, 0777)
58abb0f93cSkardel # define SERVER_PORT_NUM     123
59abb0f93cSkardel #endif
60abb0f93cSkardel 
61abb0f93cSkardel /* we use COMMAND as an autogen keyword */
62abb0f93cSkardel #ifdef COMMAND
63abb0f93cSkardel # undef COMMAND
64abb0f93cSkardel #endif
65abb0f93cSkardel 
66abb0f93cSkardel /*
67abb0f93cSkardel  * Because we potentially understand a lot of commands we will run
68abb0f93cSkardel  * interactive if connected to a terminal.
69abb0f93cSkardel  */
70abb0f93cSkardel int interactive = 0;		/* set to 1 when we should prompt */
71abb0f93cSkardel const char *prompt = "ntpq> ";	/* prompt to ask him about */
72abb0f93cSkardel 
73abb0f93cSkardel /*
74abb0f93cSkardel  * use old readvars behavior?  --old-rv processing in ntpq resets
75abb0f93cSkardel  * this value based on the presence or absence of --old-rv.  It is
76abb0f93cSkardel  * initialized to 1 here to maintain backward compatibility with
77abb0f93cSkardel  * libntpq clients such as ntpsnmpd, which are free to reset it as
78abb0f93cSkardel  * desired.
79abb0f93cSkardel  */
80abb0f93cSkardel int	old_rv = 1;
81abb0f93cSkardel 
8268dbbb44Schristos /*
8368dbbb44Schristos  * How should we display the refid?
8468dbbb44Schristos  * REFID_HASH, REFID_IPV4
8568dbbb44Schristos  */
8668dbbb44Schristos te_Refid drefid = -1;
87abb0f93cSkardel 
88abb0f93cSkardel /*
89abb0f93cSkardel  * for get_systime()
90abb0f93cSkardel  */
91abb0f93cSkardel s_char	sys_precision;		/* local clock precision (log2 s) */
92abb0f93cSkardel 
93abb0f93cSkardel /*
94abb0f93cSkardel  * Keyid used for authenticated requests.  Obtained on the fly.
95abb0f93cSkardel  */
96abb0f93cSkardel u_long info_auth_keyid = 0;
97abb0f93cSkardel 
98abb0f93cSkardel static	int	info_auth_keytype = NID_md5;	/* MD5 */
99abb0f93cSkardel static	size_t	info_auth_hashlen = 16;		/* MD5 */
100abb0f93cSkardel u_long	current_time;		/* needed by authkeys; not used */
101abb0f93cSkardel 
102abb0f93cSkardel /*
103abb0f93cSkardel  * Flag which indicates we should always send authenticated requests
104abb0f93cSkardel  */
105abb0f93cSkardel int always_auth = 0;
106abb0f93cSkardel 
107abb0f93cSkardel /*
108abb0f93cSkardel  * Flag which indicates raw mode output.
109abb0f93cSkardel  */
110abb0f93cSkardel int rawmode = 0;
111abb0f93cSkardel 
112abb0f93cSkardel /*
113abb0f93cSkardel  * Packet version number we use
114abb0f93cSkardel  */
115abb0f93cSkardel u_char pktversion = NTP_OLDVERSION + 1;
116abb0f93cSkardel 
117abb0f93cSkardel 
118abb0f93cSkardel /*
119abb0f93cSkardel  * Format values
120abb0f93cSkardel  */
121abb0f93cSkardel #define	PADDING	0
1222950cc38Schristos #define	HA	1	/* host address */
1232950cc38Schristos #define	NA	2	/* network address */
1242950cc38Schristos #define	LP	3	/* leap (print in binary) */
1252950cc38Schristos #define	RF	4	/* refid (sometimes string, sometimes not) */
126cdfa2a7eSchristos #define	AU	5	/* array of unsigned times */
1272950cc38Schristos #define FX	6	/* test flags */
1282950cc38Schristos #define TS	7	/* l_fp timestamp in hex */
1292950cc38Schristos #define	OC	8	/* integer, print in octal */
130cdfa2a7eSchristos #define	AS	9	/* array of signed times */
131cdfa2a7eSchristos #define	SN	10	/* signed number: must display +/- sign */
132abb0f93cSkardel #define	EOV	255	/* end of table */
133abb0f93cSkardel 
134abb0f93cSkardel /*
1352950cc38Schristos  * For the most part ntpq simply displays what ntpd provides in the
1362950cc38Schristos  * mostly plain-text mode 6 responses.  A few variable names are by
1372950cc38Schristos  * default "cooked" to provide more human-friendly output.
138abb0f93cSkardel  */
1392950cc38Schristos const var_format cookedvars[] = {
1402950cc38Schristos 	{ "leap",		LP },
1412950cc38Schristos 	{ "reach",		OC },
1422950cc38Schristos 	{ "refid",		RF },
1432950cc38Schristos 	{ "reftime",		TS },
1442950cc38Schristos 	{ "clock",		TS },
1452950cc38Schristos 	{ "org",		TS },
1462950cc38Schristos 	{ "rec",		TS },
1472950cc38Schristos 	{ "xmt",		TS },
1482950cc38Schristos 	{ "flash",		FX },
1492950cc38Schristos 	{ "srcadr",		HA },
1502950cc38Schristos 	{ "peeradr",		HA },	/* compat with others */
1512950cc38Schristos 	{ "dstadr",		NA },
152cdfa2a7eSchristos 	{ "filtdelay",		AU },
153cdfa2a7eSchristos 	{ "filtoffset",		AS },
154cdfa2a7eSchristos 	{ "filtdisp",		AU },
155cdfa2a7eSchristos 	{ "filterror",		AU },	/* compat with others */
156cdfa2a7eSchristos 	{ "offset",		SN },
157cdfa2a7eSchristos 	{ "frequency",		SN }
158abb0f93cSkardel };
159abb0f93cSkardel 
160abb0f93cSkardel 
161abb0f93cSkardel 
162abb0f93cSkardel /*
163abb0f93cSkardel  * flasher bits
164abb0f93cSkardel  */
165abb0f93cSkardel static const char *tstflagnames[] = {
166abb0f93cSkardel 	"pkt_dup",		/* TEST1 */
167abb0f93cSkardel 	"pkt_bogus",		/* TEST2 */
168abb0f93cSkardel 	"pkt_unsync",		/* TEST3 */
169abb0f93cSkardel 	"pkt_denied",		/* TEST4 */
170abb0f93cSkardel 	"pkt_auth",		/* TEST5 */
171abb0f93cSkardel 	"pkt_stratum",		/* TEST6 */
172abb0f93cSkardel 	"pkt_header",		/* TEST7 */
173abb0f93cSkardel 	"pkt_autokey",		/* TEST8 */
174abb0f93cSkardel 	"pkt_crypto",		/* TEST9 */
175abb0f93cSkardel 	"peer_stratum",		/* TEST10 */
176abb0f93cSkardel 	"peer_dist",		/* TEST11 */
177abb0f93cSkardel 	"peer_loop",		/* TEST12 */
178abb0f93cSkardel 	"peer_unreach"		/* TEST13 */
179abb0f93cSkardel };
180abb0f93cSkardel 
181abb0f93cSkardel 
182abb0f93cSkardel int		ntpqmain	(int,	char **);
183abb0f93cSkardel /*
184abb0f93cSkardel  * Built in command handler declarations
185abb0f93cSkardel  */
1862950cc38Schristos static	int	openhost	(const char *, int);
1872950cc38Schristos static	void	dump_hex_printable(const void *, size_t);
188abb0f93cSkardel static	int	sendpkt		(void *, size_t);
1898b8da087Schristos static	int	getresponse	(int, int, u_short *, size_t *, const char **, int);
1908b8da087Schristos static	int	sendrequest	(int, associd_t, int, size_t, const char *);
191abb0f93cSkardel static	char *	tstflags	(u_long);
192abb0f93cSkardel #ifndef BUILD_AS_LIB
193abb0f93cSkardel static	void	getcmds		(void);
194abb0f93cSkardel #ifndef SYS_WINNT
1958b8da087Schristos static	int	abortcmd	(void);
196abb0f93cSkardel #endif	/* SYS_WINNT */
197abb0f93cSkardel static	void	docmd		(const char *);
198abb0f93cSkardel static	void	tokenize	(const char *, char **, int *);
1992950cc38Schristos static	int	getarg		(const char *, int, arg_v *);
200abb0f93cSkardel #endif	/* BUILD_AS_LIB */
2012950cc38Schristos static	int	findcmd		(const char *, struct xcmd *,
2022950cc38Schristos 				 struct xcmd *, struct xcmd **);
203abb0f93cSkardel static	int	rtdatetolfp	(char *, l_fp *);
2044eea345dSchristos static	int	decodearr	(char *, int *, l_fp *, int);
205abb0f93cSkardel static	void	help		(struct parse *, FILE *);
206abb0f93cSkardel static	int	helpsort	(const void *, const void *);
207abb0f93cSkardel static	void	printusage	(struct xcmd *, FILE *);
208abb0f93cSkardel static	void	timeout		(struct parse *, FILE *);
209abb0f93cSkardel static	void	auth_delay	(struct parse *, FILE *);
210abb0f93cSkardel static	void	host		(struct parse *, FILE *);
211abb0f93cSkardel static	void	ntp_poll	(struct parse *, FILE *);
212abb0f93cSkardel static	void	keyid		(struct parse *, FILE *);
213abb0f93cSkardel static	void	keytype		(struct parse *, FILE *);
214abb0f93cSkardel static	void	passwd		(struct parse *, FILE *);
215abb0f93cSkardel static	void	hostnames	(struct parse *, FILE *);
216abb0f93cSkardel static	void	setdebug	(struct parse *, FILE *);
217abb0f93cSkardel static	void	quit		(struct parse *, FILE *);
21868dbbb44Schristos static	void	showdrefid	(struct parse *, FILE *);
219abb0f93cSkardel static	void	version		(struct parse *, FILE *);
220abb0f93cSkardel static	void	raw		(struct parse *, FILE *);
221abb0f93cSkardel static	void	cooked		(struct parse *, FILE *);
222abb0f93cSkardel static	void	authenticate	(struct parse *, FILE *);
223abb0f93cSkardel static	void	ntpversion	(struct parse *, FILE *);
22479045f13Schristos static	void	warning		(const char *, ...) NTP_PRINTF(1, 2);
22579045f13Schristos static	void	error		(const char *, ...) NTP_PRINTF(1, 2);
226abb0f93cSkardel static	u_long	getkeyid	(const char *);
227abb0f93cSkardel static	void	atoascii	(const char *, size_t, char *, size_t);
2288b8da087Schristos static	void	cookedprint	(int, size_t, const char *, int, int, FILE *);
2298b8da087Schristos static	void	rawprint	(int, size_t, const char *, int, int, FILE *);
230abb0f93cSkardel static	void	startoutput	(void);
2312950cc38Schristos static	void	output		(FILE *, const char *, const char *);
232abb0f93cSkardel static	void	endoutput	(FILE *);
233cdfa2a7eSchristos static	void	outputarr	(FILE *, char *, int, l_fp *, int);
234abb0f93cSkardel static	int	assoccmp	(const void *, const void *);
2352950cc38Schristos 	u_short	varfmt		(const char *);
236abb0f93cSkardel 	void	ntpq_custom_opt_handler(tOptions *, tOptDesc *);
237abb0f93cSkardel 
23879045f13Schristos #ifndef BUILD_AS_LIB
23979045f13Schristos static	char   *list_digest_names(void);
24079045f13Schristos static	void	on_ctrlc	(void);
24179045f13Schristos static	int	my_easprintf	(char**, const char *, ...) NTP_PRINTF(2, 3);
242*eabc0478Schristos #ifdef OPENSSL
243*eabc0478Schristos static	char   *insert_cmac	(char *list);
244*eabc0478Schristos # ifdef HAVE_EVP_MD_DO_ALL_SORTED
24579045f13Schristos static	void	list_md_fn	(const EVP_MD *m, const char *from,
24679045f13Schristos 				 const char *to, void *arg);
247*eabc0478Schristos # endif /* HAVE_EVP_MD_DO_ALL_SORTED */
248*eabc0478Schristos #endif /* OPENSSL */
24979045f13Schristos #endif /* !defined(BUILD_AS_LIB) */
25079045f13Schristos 
25179045f13Schristos 
2524eea345dSchristos /* read a character from memory and expand to integer */
2534eea345dSchristos static inline int
2544eea345dSchristos pgetc(
2554eea345dSchristos 	const char *cp
2564eea345dSchristos 	)
2574eea345dSchristos {
2584eea345dSchristos 	return (int)*(const unsigned char*)cp;
2594eea345dSchristos }
2604eea345dSchristos 
2614eea345dSchristos 
262abb0f93cSkardel 
263abb0f93cSkardel /*
264abb0f93cSkardel  * Built-in commands we understand
265abb0f93cSkardel  */
266abb0f93cSkardel struct xcmd builtins[] = {
267abb0f93cSkardel 	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
268abb0f93cSkardel 	  { "command", "", "", "" },
269abb0f93cSkardel 	  "tell the use and syntax of commands" },
270abb0f93cSkardel 	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
271abb0f93cSkardel 	  { "command", "", "", "" },
272abb0f93cSkardel 	  "tell the use and syntax of commands" },
273abb0f93cSkardel 	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
274abb0f93cSkardel 	  { "msec", "", "", "" },
275abb0f93cSkardel 	  "set the primary receive time out" },
276abb0f93cSkardel 	{ "delay",	auth_delay,	{ OPT|NTP_INT, NO, NO, NO },
277abb0f93cSkardel 	  { "msec", "", "", "" },
278abb0f93cSkardel 	  "set the delay added to encryption time stamps" },
279abb0f93cSkardel 	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
280abb0f93cSkardel 	  { "-4|-6", "hostname", "", "" },
281abb0f93cSkardel 	  "specify the host whose NTP server we talk to" },
282abb0f93cSkardel 	{ "poll",	ntp_poll,	{ OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
283abb0f93cSkardel 	  { "n", "verbose", "", "" },
284abb0f93cSkardel 	  "poll an NTP server in client mode `n' times" },
2852950cc38Schristos 	{ "passwd",	passwd,		{ OPT|NTP_STR, NO, NO, NO },
286abb0f93cSkardel 	  { "", "", "", "" },
287abb0f93cSkardel 	  "specify a password to use for authenticated requests"},
288abb0f93cSkardel 	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
289abb0f93cSkardel 	  { "yes|no", "", "", "" },
290abb0f93cSkardel 	  "specify whether hostnames or net numbers are printed"},
291abb0f93cSkardel 	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
292abb0f93cSkardel 	  { "no|more|less", "", "", "" },
293abb0f93cSkardel 	  "set/change debugging level" },
294abb0f93cSkardel 	{ "quit",	quit,		{ NO, NO, NO, NO },
295abb0f93cSkardel 	  { "", "", "", "" },
296abb0f93cSkardel 	  "exit ntpq" },
297abb0f93cSkardel 	{ "exit",	quit,		{ NO, NO, NO, NO },
298abb0f93cSkardel 	  { "", "", "", "" },
299abb0f93cSkardel 	  "exit ntpq" },
300abb0f93cSkardel 	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
301abb0f93cSkardel 	  { "key#", "", "", "" },
302abb0f93cSkardel 	  "set keyid to use for authenticated requests" },
30368dbbb44Schristos 	{ "drefid",	showdrefid,	{ OPT|NTP_STR, NO, NO, NO },
30468dbbb44Schristos 	  { "hash|ipv4", "", "", "" },
30568dbbb44Schristos 	  "display refid's as IPv4 or hash" },
306abb0f93cSkardel 	{ "version",	version,	{ NO, NO, NO, NO },
307abb0f93cSkardel 	  { "", "", "", "" },
308abb0f93cSkardel 	  "print version number" },
309abb0f93cSkardel 	{ "raw",	raw,		{ NO, NO, NO, NO },
310abb0f93cSkardel 	  { "", "", "", "" },
311abb0f93cSkardel 	  "do raw mode variable output" },
312abb0f93cSkardel 	{ "cooked",	cooked,		{ NO, NO, NO, NO },
313abb0f93cSkardel 	  { "", "", "", "" },
314abb0f93cSkardel 	  "do cooked mode variable output" },
315abb0f93cSkardel 	{ "authenticate", authenticate,	{ OPT|NTP_STR, NO, NO, NO },
316abb0f93cSkardel 	  { "yes|no", "", "", "" },
317abb0f93cSkardel 	  "always authenticate requests to this server" },
318abb0f93cSkardel 	{ "ntpversion",	ntpversion,	{ OPT|NTP_UINT, NO, NO, NO },
319abb0f93cSkardel 	  { "version number", "", "", "" },
320abb0f93cSkardel 	  "set the NTP version number to use for requests" },
321abb0f93cSkardel 	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
3225d681e99Schristos 	  { "key type %s", "", "", "" },
3235d681e99Schristos 	  NULL },
324abb0f93cSkardel 	{ 0,		0,		{ NO, NO, NO, NO },
325abb0f93cSkardel 	  { "", "", "", "" }, "" }
326abb0f93cSkardel };
327abb0f93cSkardel 
328abb0f93cSkardel 
329abb0f93cSkardel /*
330abb0f93cSkardel  * Default values we use.
331abb0f93cSkardel  */
332abb0f93cSkardel #define	DEFHOST		"localhost"	/* default host name */
3332950cc38Schristos #define	DEFTIMEOUT	5		/* wait 5 seconds for 1st pkt */
3342950cc38Schristos #define	DEFSTIMEOUT	3		/* and 3 more for each additional */
3352950cc38Schristos /*
3362950cc38Schristos  * Requests are automatically retried once, so total timeout with no
3372950cc38Schristos  * response is a bit over 2 * DEFTIMEOUT, or 10 seconds.  At the other
3382950cc38Schristos  * extreme, a request eliciting 32 packets of responses each for some
3392950cc38Schristos  * reason nearly DEFSTIMEOUT seconds after the prior in that series,
3402950cc38Schristos  * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
3412950cc38Schristos  * 93 seconds to fail each of two times, or 186 seconds.
3422950cc38Schristos  * Some commands involve a series of requests, such as "peers" and
3432950cc38Schristos  * "mrulist", so the cumulative timeouts are even longer for those.
3442950cc38Schristos  */
345abb0f93cSkardel #define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
346abb0f93cSkardel #define	LENHOSTNAME	256		/* host name is 256 characters long */
347abb0f93cSkardel #define	MAXCMDS		100		/* maximum commands on cmd line */
348abb0f93cSkardel #define	MAXHOSTS	200		/* maximum hosts on cmd line */
349abb0f93cSkardel #define	MAXLINE		512		/* maximum line length */
350abb0f93cSkardel #define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
351abb0f93cSkardel #define	MAXVARLEN	256		/* maximum length of a variable name */
3522950cc38Schristos #define	MAXVALLEN	2048		/* maximum length of a variable value */
353abb0f93cSkardel #define	MAXOUTLINE	72		/* maximum length of an output line */
354abb0f93cSkardel #define SCREENWIDTH	76		/* nominal screen width in columns */
355abb0f93cSkardel 
356abb0f93cSkardel /*
357abb0f93cSkardel  * Some variables used and manipulated locally
358abb0f93cSkardel  */
359abb0f93cSkardel struct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
360abb0f93cSkardel struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
361abb0f93cSkardel l_fp delay_time;				/* delay time */
362abb0f93cSkardel char currenthost[LENHOSTNAME];			/* current host name */
3633123f114Skardel int currenthostisnum;				/* is prior text from IP? */
364a2545411Skardel struct sockaddr_in hostaddr;			/* host address */
365abb0f93cSkardel int showhostnames = 1;				/* show host names by default */
366ea66d795Schristos int wideremote = 0;				/* show wide remote names? */
367abb0f93cSkardel 
368abb0f93cSkardel int ai_fam_templ;				/* address family */
369abb0f93cSkardel int ai_fam_default;				/* default address family */
370abb0f93cSkardel SOCKET sockfd;					/* fd socket is opened on */
371abb0f93cSkardel int havehost = 0;				/* set to 1 when host open */
372abb0f93cSkardel int s_port = 0;
373abb0f93cSkardel struct servent *server_entry = NULL;		/* server entry for ntp */
374abb0f93cSkardel 
375abb0f93cSkardel 
376abb0f93cSkardel /*
377abb0f93cSkardel  * Sequence number used for requests.  It is incremented before
378abb0f93cSkardel  * it is used.
379abb0f93cSkardel  */
380abb0f93cSkardel u_short sequence;
381abb0f93cSkardel 
382abb0f93cSkardel /*
383abb0f93cSkardel  * Holds data returned from queries.  Declare buffer long to be sure of
384abb0f93cSkardel  * alignment.
385abb0f93cSkardel  */
386abb0f93cSkardel #define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
387abb0f93cSkardel long pktdata[DATASIZE/sizeof(long)];
388abb0f93cSkardel 
389abb0f93cSkardel /*
3902950cc38Schristos  * assoc_cache[] is a dynamic array which allows references to
3912950cc38Schristos  * associations using &1 ... &N for n associations, avoiding manual
3922950cc38Schristos  * lookup of the current association IDs for a given ntpd.  It also
3932950cc38Schristos  * caches the status word for each association, retrieved incidentally.
394abb0f93cSkardel  */
3952950cc38Schristos struct association *	assoc_cache;
3962950cc38Schristos u_int assoc_cache_slots;/* count of allocated array entries */
3972950cc38Schristos u_int numassoc;		/* number of cached associations */
398abb0f93cSkardel 
399abb0f93cSkardel /*
400abb0f93cSkardel  * For commands typed on the command line (with the -c option)
401abb0f93cSkardel  */
4025d681e99Schristos size_t numcmds = 0;
403*eabc0478Schristos size_t defcmds = 0;        /* Options on the command line are 'defined'! */
404*eabc0478Schristos char *ccmds[MAXCMDS];
405*eabc0478Schristos #define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = estrdup(cp)
406abb0f93cSkardel 
407abb0f93cSkardel /*
408abb0f93cSkardel  * When multiple hosts are specified.
409abb0f93cSkardel  */
410abb0f93cSkardel 
4112950cc38Schristos u_int numhosts;
4122950cc38Schristos 
4132950cc38Schristos chost chosts[MAXHOSTS];
4142950cc38Schristos #define	ADDHOST(cp)						\
4152950cc38Schristos 	do {							\
4162950cc38Schristos 		if (numhosts < MAXHOSTS) {			\
4172950cc38Schristos 			chosts[numhosts].name = (cp);		\
4182950cc38Schristos 			chosts[numhosts].fam = ai_fam_templ;	\
4192950cc38Schristos 			numhosts++;				\
4202950cc38Schristos 		}						\
4212950cc38Schristos 	} while (0)
422abb0f93cSkardel 
423abb0f93cSkardel /*
424abb0f93cSkardel  * Macro definitions we use
425abb0f93cSkardel  */
426abb0f93cSkardel #define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
427abb0f93cSkardel #define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
428abb0f93cSkardel #define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
429abb0f93cSkardel 
430abb0f93cSkardel /*
43179045f13Schristos  * Jump buffer for longjumping back to the command level.
43279045f13Schristos  *
43379045f13Schristos  * Since we do this from a signal handler, we use 'sig{set,long}jmp()'
43479045f13Schristos  * if available. The signal is blocked by default during the excution of
43579045f13Schristos  * a signal handler, and it is unspecified if '{set,long}jmp()' save and
43679045f13Schristos  * restore the signal mask. They do on BSD, it depends on the GLIBC
43779045f13Schristos  * version on Linux, and the gods know what happens on other OSes...
43879045f13Schristos  *
43979045f13Schristos  * So we use the 'sig{set,long}jmp()' functions where available, because
44079045f13Schristos  * for them the semantics are well-defined. If we have to fall back to
44179045f13Schristos  * '{set,long}jmp()', the CTRL-C handling might be a bit erratic.
442abb0f93cSkardel  */
44379045f13Schristos #if HAVE_DECL_SIGSETJMP && HAVE_DECL_SIGLONGJMP
44479045f13Schristos # define JMP_BUF	sigjmp_buf
44579045f13Schristos # define SETJMP(x)	sigsetjmp((x), 1)
44679045f13Schristos # define LONGJMP(x, v)	siglongjmp((x),(v))
44779045f13Schristos #else
44879045f13Schristos # define JMP_BUF	jmp_buf
44979045f13Schristos # define SETJMP(x)	setjmp((x))
45079045f13Schristos # define LONGJMP(x, v)	longjmp((x),(v))
45179045f13Schristos #endif
452*eabc0478Schristos 
453*eabc0478Schristos #ifndef BUILD_AS_LIB
45479045f13Schristos static	JMP_BUF		interrupt_buf;
45579045f13Schristos static	volatile int	jump = 0;
456*eabc0478Schristos #endif
457abb0f93cSkardel 
458abb0f93cSkardel /*
459abb0f93cSkardel  * Points at file being currently printed into
460abb0f93cSkardel  */
46179045f13Schristos FILE *current_output = NULL;
462abb0f93cSkardel 
463abb0f93cSkardel /*
464abb0f93cSkardel  * Command table imported from ntpdc_ops.c
465abb0f93cSkardel  */
466abb0f93cSkardel extern struct xcmd opcmds[];
467abb0f93cSkardel 
468af12ab5eSchristos char const *progname;
469abb0f93cSkardel 
470abb0f93cSkardel #ifdef NO_MAIN_ALLOWED
471abb0f93cSkardel #ifndef BUILD_AS_LIB
472abb0f93cSkardel CALL(ntpq,"ntpq",ntpqmain);
473abb0f93cSkardel 
474abb0f93cSkardel void clear_globals(void)
475abb0f93cSkardel {
476abb0f93cSkardel 	extern int ntp_optind;
477abb0f93cSkardel 	showhostnames = 0;	/* don'tshow host names by default */
478abb0f93cSkardel 	ntp_optind = 0;
479abb0f93cSkardel 	server_entry = NULL;	/* server entry for ntp */
480abb0f93cSkardel 	havehost = 0;		/* set to 1 when host open */
481abb0f93cSkardel 	numassoc = 0;		/* number of cached associations */
482abb0f93cSkardel 	numcmds = 0;
483abb0f93cSkardel 	numhosts = 0;
484abb0f93cSkardel }
485abb0f93cSkardel #endif /* !BUILD_AS_LIB */
486abb0f93cSkardel #endif /* NO_MAIN_ALLOWED */
487abb0f93cSkardel 
488abb0f93cSkardel /*
489abb0f93cSkardel  * main - parse arguments and handle options
490abb0f93cSkardel  */
491abb0f93cSkardel #ifndef NO_MAIN_ALLOWED
492abb0f93cSkardel int
493abb0f93cSkardel main(
494abb0f93cSkardel 	int argc,
495abb0f93cSkardel 	char *argv[]
496abb0f93cSkardel 	)
497abb0f93cSkardel {
498abb0f93cSkardel 	return ntpqmain(argc, argv);
499abb0f93cSkardel }
500abb0f93cSkardel #endif
501abb0f93cSkardel 
5024eea345dSchristos 
503abb0f93cSkardel #ifndef BUILD_AS_LIB
504abb0f93cSkardel int
505abb0f93cSkardel ntpqmain(
506abb0f93cSkardel 	int argc,
507abb0f93cSkardel 	char *argv[]
508abb0f93cSkardel 	)
509abb0f93cSkardel {
5102950cc38Schristos 	u_int ihost;
5115d681e99Schristos 	size_t icmd;
5122950cc38Schristos 
513abb0f93cSkardel 
514abb0f93cSkardel #ifdef SYS_VXWORKS
515abb0f93cSkardel 	clear_globals();
516abb0f93cSkardel 	taskPrioritySet(taskIdSelf(), 100 );
517abb0f93cSkardel #endif
518abb0f93cSkardel 
519abb0f93cSkardel 	delay_time.l_ui = 0;
520abb0f93cSkardel 	delay_time.l_uf = DEFDELAY;
521abb0f93cSkardel 
522abb0f93cSkardel 	init_lib();	/* sets up ipv4_works, ipv6_works */
523abb0f93cSkardel 	ssl_applink();
5242950cc38Schristos 	init_auth();
525abb0f93cSkardel 
526abb0f93cSkardel 	/* Check to see if we have IPv6. Otherwise default to IPv4 */
527abb0f93cSkardel 	if (!ipv6_works)
528abb0f93cSkardel 		ai_fam_default = AF_INET;
529abb0f93cSkardel 
5305d681e99Schristos 	/* Fixup keytype's help based on available digest names */
5315d681e99Schristos 
5325d681e99Schristos 	{
5335d681e99Schristos 	    char *list;
5345d681e99Schristos 	    char *msg;
5355d681e99Schristos 
5365d681e99Schristos 	    list = list_digest_names();
5374eea345dSchristos 
5384eea345dSchristos 	    for (icmd = 0; icmd < sizeof(builtins)/sizeof(*builtins); icmd++) {
5394eea345dSchristos 		if (strcmp("keytype", builtins[icmd].keyword) == 0) {
5405d681e99Schristos 		    break;
5415d681e99Schristos 		}
5424eea345dSchristos 	    }
5435d681e99Schristos 
5445d681e99Schristos 	    /* CID: 1295478 */
5455d681e99Schristos 	    /* This should only "trip" if "keytype" is removed from builtins */
5464eea345dSchristos 	    INSIST(icmd < sizeof(builtins)/sizeof(*builtins));
5475d681e99Schristos 
5485d681e99Schristos #ifdef OPENSSL
5495d681e99Schristos 	    builtins[icmd].desc[0] = "digest-name";
55068dbbb44Schristos 	    my_easprintf(&msg,
55168dbbb44Schristos 			 "set key type to use for authenticated requests, one of:%s",
55268dbbb44Schristos 			 list);
5535d681e99Schristos #else
5545d681e99Schristos 	    builtins[icmd].desc[0] = "md5";
55568dbbb44Schristos 	    my_easprintf(&msg,
55668dbbb44Schristos 			 "set key type to use for authenticated requests (%s)",
5575d681e99Schristos 			 list);
55868dbbb44Schristos #endif
5595d681e99Schristos 	    builtins[icmd].comment = msg;
5605d681e99Schristos 	    free(list);
5615d681e99Schristos 	}
5625d681e99Schristos 
563abb0f93cSkardel 	progname = argv[0];
564abb0f93cSkardel 
565abb0f93cSkardel 	{
5663123f114Skardel 		int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
567abb0f93cSkardel 		argc -= optct;
568abb0f93cSkardel 		argv += optct;
569abb0f93cSkardel 	}
570abb0f93cSkardel 
571abb0f93cSkardel 	/*
572abb0f93cSkardel 	 * Process options other than -c and -p, which are specially
573abb0f93cSkardel 	 * handled by ntpq_custom_opt_handler().
574abb0f93cSkardel 	 */
575abb0f93cSkardel 
5762950cc38Schristos 	debug = OPT_VALUE_SET_DEBUG_LEVEL;
577abb0f93cSkardel 
578abb0f93cSkardel 	if (HAVE_OPT(IPV4))
579abb0f93cSkardel 		ai_fam_templ = AF_INET;
580abb0f93cSkardel 	else if (HAVE_OPT(IPV6))
581abb0f93cSkardel 		ai_fam_templ = AF_INET6;
582abb0f93cSkardel 	else
583abb0f93cSkardel 		ai_fam_templ = ai_fam_default;
584abb0f93cSkardel 
585abb0f93cSkardel 	if (HAVE_OPT(INTERACTIVE))
586abb0f93cSkardel 		interactive = 1;
587abb0f93cSkardel 
588abb0f93cSkardel 	if (HAVE_OPT(NUMERIC))
589abb0f93cSkardel 		showhostnames = 0;
590abb0f93cSkardel 
591ea66d795Schristos 	if (HAVE_OPT(WIDE))
592ea66d795Schristos 		wideremote = 1;
593ea66d795Schristos 
594abb0f93cSkardel 	old_rv = HAVE_OPT(OLD_RV);
595abb0f93cSkardel 
59668dbbb44Schristos 	drefid = OPT_VALUE_REFID;
59768dbbb44Schristos 
5982950cc38Schristos 	if (0 == argc) {
599abb0f93cSkardel 		ADDHOST(DEFHOST);
600abb0f93cSkardel 	} else {
6012950cc38Schristos 		for (ihost = 0; ihost < (u_int)argc; ihost++) {
6022950cc38Schristos 			if ('-' == *argv[ihost]) {
6032950cc38Schristos 				//
6042950cc38Schristos 				// If I really cared I'd also check:
6052950cc38Schristos 				// 0 == argv[ihost][2]
6062950cc38Schristos 				//
6072950cc38Schristos 				// and there are other cases as well...
6082950cc38Schristos 				//
6092950cc38Schristos 				if ('4' == argv[ihost][1]) {
6102950cc38Schristos 					ai_fam_templ = AF_INET;
6112950cc38Schristos 					continue;
6122950cc38Schristos 				} else if ('6' == argv[ihost][1]) {
6132950cc38Schristos 					ai_fam_templ = AF_INET6;
6142950cc38Schristos 					continue;
6152950cc38Schristos 				} else {
6162950cc38Schristos 					// XXX Throw a usage error
6172950cc38Schristos 				}
6182950cc38Schristos 			}
6192950cc38Schristos 			ADDHOST(argv[ihost]);
6202950cc38Schristos 		}
621abb0f93cSkardel 	}
622abb0f93cSkardel 
623*eabc0478Schristos 	if (defcmds == 0 && interactive == 0
624abb0f93cSkardel 	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
625abb0f93cSkardel 		interactive = 1;
626abb0f93cSkardel 	}
627abb0f93cSkardel 
6288b8da087Schristos 	set_ctrl_c_hook(on_ctrlc);
629abb0f93cSkardel #ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
630abb0f93cSkardel 	if (interactive)
6318b8da087Schristos 		push_ctrl_c_handler(abortcmd);
632abb0f93cSkardel #endif /* SYS_WINNT */
633abb0f93cSkardel 
634*eabc0478Schristos 	if (numcmds > 0) {
635abb0f93cSkardel 		for (ihost = 0; ihost < numhosts; ihost++) {
6364eea345dSchristos 			if (openhost(chosts[ihost].name, chosts[ihost].fam)) {
63779045f13Schristos 				if (ihost && current_output)
6384eea345dSchristos 					fputc('\n', current_output);
6394eea345dSchristos 				for (icmd = 0; icmd < numcmds; icmd++) {
64079045f13Schristos 					if (icmd && current_output)
6414eea345dSchristos 						fputc('\n', current_output);
642abb0f93cSkardel 					docmd(ccmds[icmd]);
643abb0f93cSkardel 				}
644abb0f93cSkardel 			}
6454eea345dSchristos 		}
646*eabc0478Schristos 		/* Release memory allocated in ADDCMD */
647*eabc0478Schristos 		for (icmd = 0; icmd < numcmds; icmd++)
648*eabc0478Schristos 			free(ccmds[icmd]);
649*eabc0478Schristos 	}
650*eabc0478Schristos 
651*eabc0478Schristos 	if (defcmds == 0) {        /* No command line commands, so go interactive */
652*eabc0478Schristos 		(void) openhost(chosts[0].name, chosts[0].fam);
653*eabc0478Schristos 		getcmds();
6544eea345dSchristos 	}
655abb0f93cSkardel #ifdef SYS_WINNT
656abb0f93cSkardel 	WSACleanup();
657abb0f93cSkardel #endif /* SYS_WINNT */
658abb0f93cSkardel 	return 0;
659abb0f93cSkardel }
660abb0f93cSkardel #endif /* !BUILD_AS_LIB */
661abb0f93cSkardel 
662abb0f93cSkardel /*
663abb0f93cSkardel  * openhost - open a socket to a host
664abb0f93cSkardel  */
665abb0f93cSkardel static	int
666abb0f93cSkardel openhost(
6672950cc38Schristos 	const char *hname,
6682950cc38Schristos 	int	    fam
669abb0f93cSkardel 	)
670abb0f93cSkardel {
6712950cc38Schristos 	const char svc[] = "ntp";
672abb0f93cSkardel 	char temphost[LENHOSTNAME];
67379045f13Schristos 	int a_info;
6742950cc38Schristos 	struct addrinfo hints, *ai;
6752950cc38Schristos 	sockaddr_u addr;
6762950cc38Schristos 	size_t octets;
67779045f13Schristos 	const char *cp;
678abb0f93cSkardel 	char name[LENHOSTNAME];
679abb0f93cSkardel 
680abb0f93cSkardel 	/*
681abb0f93cSkardel 	 * We need to get by the [] if they were entered
682abb0f93cSkardel 	 */
68379045f13Schristos 	if (*hname == '[') {
68479045f13Schristos 		cp = strchr(hname + 1, ']');
68579045f13Schristos 		if (!cp || (octets = (size_t)(cp - hname) - 1) >= sizeof(name)) {
68679045f13Schristos 			errno = EINVAL;
68779045f13Schristos 			warning("%s", "bad hostname/address");
688abb0f93cSkardel 			return 0;
689abb0f93cSkardel 		}
69079045f13Schristos 		memcpy(name, hname + 1, octets);
69179045f13Schristos 		name[octets] = '\0';
69279045f13Schristos 		hname = name;
693abb0f93cSkardel 	}
694abb0f93cSkardel 
695abb0f93cSkardel 	/*
696abb0f93cSkardel 	 * First try to resolve it as an ip address and if that fails,
697abb0f93cSkardel 	 * do a fullblown (dns) lookup. That way we only use the dns
698abb0f93cSkardel 	 * when it is needed and work around some implementations that
699abb0f93cSkardel 	 * will return an "IPv4-mapped IPv6 address" address if you
700abb0f93cSkardel 	 * give it an IPv4 address to lookup.
701abb0f93cSkardel 	 */
7023123f114Skardel 	ZERO(hints);
7032950cc38Schristos 	hints.ai_family = fam;
704abb0f93cSkardel 	hints.ai_protocol = IPPROTO_UDP;
705abb0f93cSkardel 	hints.ai_socktype = SOCK_DGRAM;
7063123f114Skardel 	hints.ai_flags = Z_AI_NUMERICHOST;
7072950cc38Schristos 	ai = NULL;
708abb0f93cSkardel 
7092950cc38Schristos 	a_info = getaddrinfo(hname, svc, &hints, &ai);
710abb0f93cSkardel 	if (a_info == EAI_NONAME
711abb0f93cSkardel #ifdef EAI_NODATA
712abb0f93cSkardel 	    || a_info == EAI_NODATA
713abb0f93cSkardel #endif
714abb0f93cSkardel 	   ) {
715abb0f93cSkardel 		hints.ai_flags = AI_CANONNAME;
716abb0f93cSkardel #ifdef AI_ADDRCONFIG
717abb0f93cSkardel 		hints.ai_flags |= AI_ADDRCONFIG;
718abb0f93cSkardel #endif
7192950cc38Schristos 		a_info = getaddrinfo(hname, svc, &hints, &ai);
720abb0f93cSkardel 	}
721abb0f93cSkardel #ifdef AI_ADDRCONFIG
722*eabc0478Schristos 	/*
723*eabc0478Schristos 	 * Some older implementations don't like AI_ADDRCONFIG.
724*eabc0478Schristos 	 * Some versions of Windows return WSANO_DATA when there is no
725*eabc0478Schristos 	 * global address and AI_ADDRCONFIG is used.  AI_ADDRCONFIG
726*eabc0478Schristos 	 * is useful to short-circuit DNS lookups for IP protocols
727*eabc0478Schristos 	 * for which the host has no local addresses.  Windows
728*eabc0478Schristos 	 * unfortunately instead interprets AI_ADDRCONFIG to relate
729*eabc0478Schristos 	 * to off-host connectivity and so fails lookup when
730*eabc0478Schristos 	 * localhost works.
731*eabc0478Schristos 	 * To further muddy matters, some versions of WS2tcpip.h
732*eabc0478Schristos 	 * comment out #define EAI_NODATA WSANODATA claiming it
733*eabc0478Schristos 	 * was removed from RFC 2553bis and mentioning a need to
734*eabc0478Schristos 	 * contact the authors to find out why, but "helpfully"
735*eabc0478Schristos 	 * #defines EAI_NODATA EAI_NONAME   (== WSAHOST_NOT_FOUND)
736*eabc0478Schristos 	 * So we get more ugly platform-specific workarounds.
737*eabc0478Schristos 	 */
738*eabc0478Schristos 	if (
739*eabc0478Schristos #if defined(WIN32)
740*eabc0478Schristos 	    WSANO_DATA == a_info || EAI_NONAME == a_info ||
741*eabc0478Schristos #endif
742*eabc0478Schristos 	    EAI_BADFLAGS == a_info) {
7432950cc38Schristos 		hints.ai_flags &= ~AI_ADDRCONFIG;
7442950cc38Schristos 		a_info = getaddrinfo(hname, svc, &hints, &ai);
745abb0f93cSkardel 	}
746abb0f93cSkardel #endif
747abb0f93cSkardel 	if (a_info != 0) {
7482950cc38Schristos 		fprintf(stderr, "%s\n", gai_strerror(a_info));
749abb0f93cSkardel 		return 0;
750abb0f93cSkardel 	}
751abb0f93cSkardel 
7522950cc38Schristos 	INSIST(ai != NULL);
7532950cc38Schristos 	ZERO(addr);
7542950cc38Schristos 	octets = min(sizeof(addr), ai->ai_addrlen);
7552950cc38Schristos 	memcpy(&addr, ai->ai_addr, octets);
7562950cc38Schristos 
7572950cc38Schristos 	if (ai->ai_canonname == NULL) {
7582950cc38Schristos 		strlcpy(temphost, stoa(&addr), sizeof(temphost));
7593123f114Skardel 		currenthostisnum = TRUE;
760abb0f93cSkardel 	} else {
7612950cc38Schristos 		strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
7623123f114Skardel 		currenthostisnum = FALSE;
763abb0f93cSkardel 	}
764abb0f93cSkardel 
765abb0f93cSkardel 	if (debug > 2)
7662950cc38Schristos 		printf("Opening host %s (%s)\n",
7672950cc38Schristos 			temphost,
7682950cc38Schristos 			(ai->ai_family == AF_INET)
7692950cc38Schristos 			? "AF_INET"
7702950cc38Schristos 			: (ai->ai_family == AF_INET6)
7712950cc38Schristos 			  ? "AF_INET6"
7722950cc38Schristos 			  : "AF-???"
7732950cc38Schristos 			);
774abb0f93cSkardel 
775abb0f93cSkardel 	if (havehost == 1) {
776abb0f93cSkardel 		if (debug > 2)
777abb0f93cSkardel 			printf("Closing old host %s\n", currenthost);
7782950cc38Schristos 		closesocket(sockfd);
779abb0f93cSkardel 		havehost = 0;
780abb0f93cSkardel 	}
7812950cc38Schristos 	strlcpy(currenthost, temphost, sizeof(currenthost));
782abb0f93cSkardel 
783abb0f93cSkardel 	/* port maps to the same location in both families */
7842950cc38Schristos 	s_port = NSRCPORT(&addr);
785abb0f93cSkardel #ifdef SYS_VXWORKS
786abb0f93cSkardel 	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
787abb0f93cSkardel 	if (ai->ai_family == AF_INET)
788abb0f93cSkardel 		*(struct sockaddr_in *)&hostaddr=
789abb0f93cSkardel 			*((struct sockaddr_in *)ai->ai_addr);
790abb0f93cSkardel 	else
791abb0f93cSkardel 		*(struct sockaddr_in6 *)&hostaddr=
792abb0f93cSkardel 			*((struct sockaddr_in6 *)ai->ai_addr);
793abb0f93cSkardel #endif /* SYS_VXWORKS */
794abb0f93cSkardel 
795abb0f93cSkardel #ifdef SYS_WINNT
796abb0f93cSkardel 	{
797abb0f93cSkardel 		int optionValue = SO_SYNCHRONOUS_NONALERT;
798abb0f93cSkardel 		int err;
799abb0f93cSkardel 
800abb0f93cSkardel 		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
8014eea345dSchristos 				 (void *)&optionValue, sizeof(optionValue));
802abb0f93cSkardel 		if (err) {
8032950cc38Schristos 			mfprintf(stderr,
804abb0f93cSkardel 				 "setsockopt(SO_SYNCHRONOUS_NONALERT)"
8052950cc38Schristos 				 " error: %m\n");
8062950cc38Schristos 			freeaddrinfo(ai);
807abb0f93cSkardel 			exit(1);
808abb0f93cSkardel 		}
809abb0f93cSkardel 	}
810abb0f93cSkardel #endif /* SYS_WINNT */
811abb0f93cSkardel 
8122950cc38Schristos 	sockfd = socket(ai->ai_family, ai->ai_socktype,
8132950cc38Schristos 			ai->ai_protocol);
814abb0f93cSkardel 	if (sockfd == INVALID_SOCKET) {
815058f310fSchristos 		error("socket");
8162950cc38Schristos 		freeaddrinfo(ai);
8172950cc38Schristos 		return 0;
818abb0f93cSkardel 	}
819abb0f93cSkardel 
820abb0f93cSkardel 
821abb0f93cSkardel #ifdef NEED_RCVBUF_SLOP
822abb0f93cSkardel # ifdef SO_RCVBUF
823abb0f93cSkardel 	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
824abb0f93cSkardel 	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
8254eea345dSchristos 		       (void *)&rbufsize, sizeof(int)) == -1)
826058f310fSchristos 		error("setsockopt");
827abb0f93cSkardel 	}
828abb0f93cSkardel # endif
829abb0f93cSkardel #endif
830abb0f93cSkardel 
8312950cc38Schristos 	if
832abb0f93cSkardel #ifdef SYS_VXWORKS
8332950cc38Schristos 	   (connect(sockfd, (struct sockaddr *)&hostaddr,
834abb0f93cSkardel 		    sizeof(hostaddr)) == -1)
835abb0f93cSkardel #else
8362950cc38Schristos 	   (connect(sockfd, (struct sockaddr *)ai->ai_addr,
837abb0f93cSkardel 		ai->ai_addrlen) == -1)
838abb0f93cSkardel #endif /* SYS_VXWORKS */
8392950cc38Schristos 	{
840058f310fSchristos 		error("connect");
8412950cc38Schristos 		freeaddrinfo(ai);
8422950cc38Schristos 		return 0;
8432950cc38Schristos 	}
844abb0f93cSkardel 	freeaddrinfo(ai);
845abb0f93cSkardel 	havehost = 1;
8462950cc38Schristos 	numassoc = 0;
8472950cc38Schristos 
848abb0f93cSkardel 	return 1;
849abb0f93cSkardel }
850abb0f93cSkardel 
851abb0f93cSkardel 
8522950cc38Schristos static void
8532950cc38Schristos dump_hex_printable(
8542950cc38Schristos 	const void *	data,
8552950cc38Schristos 	size_t		len
8562950cc38Schristos 	)
8572950cc38Schristos {
858ccc794f0Schristos 	/* every line shows at most 16 bytes, so we need a buffer of
859ccc794f0Schristos 	 *   4 * 16 (2 xdigits, 1 char, one sep for xdigits)
860ccc794f0Schristos 	 * + 2 * 1  (block separators)
861ccc794f0Schristos 	 * + <LF> + <NUL>
862ccc794f0Schristos 	 *---------------
863ccc794f0Schristos 	 *  68 bytes
864ccc794f0Schristos 	 */
865ccc794f0Schristos 	static const char s_xdig[16] = "0123456789ABCDEF";
8662950cc38Schristos 
867ccc794f0Schristos 	char lbuf[68];
868ccc794f0Schristos 	int  ch, rowlen;
869ccc794f0Schristos 	const u_char * cdata = data;
870ccc794f0Schristos 	char *xptr, *pptr;
871ccc794f0Schristos 
872ccc794f0Schristos 	while (len) {
873ccc794f0Schristos 		memset(lbuf, ' ', sizeof(lbuf));
874ccc794f0Schristos 		xptr = lbuf;
875ccc794f0Schristos 		pptr = lbuf + 3*16 + 2;
876ccc794f0Schristos 
877ccc794f0Schristos 		rowlen = (len > 16) ? 16 : (int)len;
8782950cc38Schristos 		len -= rowlen;
879ccc794f0Schristos 
880ccc794f0Schristos 		do {
881ccc794f0Schristos 			ch = *cdata++;
882ccc794f0Schristos 
883ccc794f0Schristos 			*xptr++ = s_xdig[ch >> 4  ];
884ccc794f0Schristos 			*xptr++ = s_xdig[ch & 0x0F];
885ccc794f0Schristos 			if (++xptr == lbuf + 3*8)
886ccc794f0Schristos 				++xptr;
887ccc794f0Schristos 
888ccc794f0Schristos 			*pptr++ = isprint(ch) ? (char)ch : '.';
889ccc794f0Schristos 		} while (--rowlen);
890ccc794f0Schristos 
891ccc794f0Schristos 		*pptr++ = '\n';
892ccc794f0Schristos 		*pptr   = '\0';
893ccc794f0Schristos 		fputs(lbuf, stdout);
8942950cc38Schristos 	}
8952950cc38Schristos }
8962950cc38Schristos 
8972950cc38Schristos 
898abb0f93cSkardel /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
899abb0f93cSkardel /*
900abb0f93cSkardel  * sendpkt - send a packet to the remote host
901abb0f93cSkardel  */
902abb0f93cSkardel static int
903abb0f93cSkardel sendpkt(
904abb0f93cSkardel 	void *	xdata,
905abb0f93cSkardel 	size_t	xdatalen
906abb0f93cSkardel 	)
907abb0f93cSkardel {
908abb0f93cSkardel 	if (debug >= 3)
909e19314b7Schristos 		printf("Sending %zu octets\n", xdatalen);
910abb0f93cSkardel 
9118b8da087Schristos 	if (send(sockfd, xdata, xdatalen, 0) == -1) {
912058f310fSchristos 		warning("write to %s failed", currenthost);
913abb0f93cSkardel 		return -1;
914abb0f93cSkardel 	}
915abb0f93cSkardel 
916abb0f93cSkardel 	if (debug >= 4) {
9172950cc38Schristos 		printf("Request packet:\n");
9182950cc38Schristos 		dump_hex_printable(xdata, xdatalen);
919abb0f93cSkardel 	}
920abb0f93cSkardel 	return 0;
921abb0f93cSkardel }
922abb0f93cSkardel 
923abb0f93cSkardel /*
924abb0f93cSkardel  * getresponse - get a (series of) response packet(s) and return the data
925abb0f93cSkardel  */
926abb0f93cSkardel static int
927abb0f93cSkardel getresponse(
928abb0f93cSkardel 	int opcode,
929abb0f93cSkardel 	int associd,
930abb0f93cSkardel 	u_short *rstatus,
9318b8da087Schristos 	size_t *rsize,
9323123f114Skardel 	const char **rdata,
933abb0f93cSkardel 	int timeo
934abb0f93cSkardel 	)
935abb0f93cSkardel {
936abb0f93cSkardel 	struct ntp_control rpkt;
937abb0f93cSkardel 	struct sock_timeval tvo;
938abb0f93cSkardel 	u_short offsets[MAXFRAGS+1];
939abb0f93cSkardel 	u_short counts[MAXFRAGS+1];
940abb0f93cSkardel 	u_short offset;
941abb0f93cSkardel 	u_short count;
9423123f114Skardel 	size_t numfrags;
9433123f114Skardel 	size_t f;
9443123f114Skardel 	size_t ff;
945abb0f93cSkardel 	int seenlastfrag;
946abb0f93cSkardel 	int shouldbesize;
947abb0f93cSkardel 	fd_set fds;
948abb0f93cSkardel 	int n;
9492950cc38Schristos 	int errcode;
95068dbbb44Schristos 	/* absolute timeout checks. Not 'time_t' by intention! */
95168dbbb44Schristos 	uint32_t tobase;	/* base value for timeout */
95268dbbb44Schristos 	uint32_t tospan;	/* timeout span (max delay) */
95368dbbb44Schristos 	uint32_t todiff;	/* current delay */
954abb0f93cSkardel 
955ccc794f0Schristos 	memset(offsets, 0, sizeof(offsets));
956ccc794f0Schristos 	memset(counts , 0, sizeof(counts ));
957ccc794f0Schristos 
958abb0f93cSkardel 	/*
959abb0f93cSkardel 	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
960abb0f93cSkardel 	 * back in response to the request.  We peel the data out of
961abb0f93cSkardel 	 * each packet and collect it in one long block.  When the last
962abb0f93cSkardel 	 * packet in the sequence is received we'll know how much data we
963abb0f93cSkardel 	 * should have had.  Note we use one long time out, should reconsider.
964abb0f93cSkardel 	 */
965abb0f93cSkardel 	*rsize = 0;
966abb0f93cSkardel 	if (rstatus)
967abb0f93cSkardel 		*rstatus = 0;
968abb0f93cSkardel 	*rdata = (char *)pktdata;
969abb0f93cSkardel 
970abb0f93cSkardel 	numfrags = 0;
971abb0f93cSkardel 	seenlastfrag = 0;
972abb0f93cSkardel 
97368dbbb44Schristos 	tobase = (uint32_t)time(NULL);
97468dbbb44Schristos 
975abb0f93cSkardel 	FD_ZERO(&fds);
976abb0f93cSkardel 
977abb0f93cSkardel 	/*
978abb0f93cSkardel 	 * Loop until we have an error or a complete response.  Nearly all
9793123f114Skardel 	 * code paths to loop again use continue.
980abb0f93cSkardel 	 */
981abb0f93cSkardel 	for (;;) {
982abb0f93cSkardel 
983abb0f93cSkardel 		if (numfrags == 0)
984abb0f93cSkardel 			tvo = tvout;
985abb0f93cSkardel 		else
986abb0f93cSkardel 			tvo = tvsout;
98768dbbb44Schristos 		tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
988abb0f93cSkardel 
989abb0f93cSkardel 		FD_SET(sockfd, &fds);
9903123f114Skardel 		n = select(sockfd+1, &fds, NULL, NULL, &tvo);
991abb0f93cSkardel 		if (n == -1) {
99268dbbb44Schristos #if !defined(SYS_WINNT) && defined(EINTR)
99368dbbb44Schristos 			/* Windows does not know about EINTR (until very
99468dbbb44Schristos 			 * recently) and the handling of console events
99568dbbb44Schristos 			 * is *very* different from POSIX/UNIX signal
99668dbbb44Schristos 			 * handling anyway.
99768dbbb44Schristos 			 *
99868dbbb44Schristos 			 * Under non-windows targets we map EINTR as
99968dbbb44Schristos 			 * 'last packet was received' and try to exit
100068dbbb44Schristos 			 * the receive sequence.
100168dbbb44Schristos 			 */
100268dbbb44Schristos 			if (errno == EINTR) {
100368dbbb44Schristos 				seenlastfrag = 1;
100468dbbb44Schristos 				goto maybe_final;
100568dbbb44Schristos 			}
100668dbbb44Schristos #endif
1007058f310fSchristos 			warning("select fails");
1008abb0f93cSkardel 			return -1;
1009abb0f93cSkardel 		}
101068dbbb44Schristos 
101168dbbb44Schristos 		/*
101268dbbb44Schristos 		 * Check if this is already too late. Trash the data and
101368dbbb44Schristos 		 * fake a timeout if this is so.
101468dbbb44Schristos 		 */
101568dbbb44Schristos 		todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
101668dbbb44Schristos 		if ((n > 0) && (todiff > tospan)) {
101768dbbb44Schristos 			n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
1018ccc794f0Schristos 			n -= n; /* faked timeout return from 'select()',
1019ccc794f0Schristos 				 * execute RMW cycle on 'n'
1020ccc794f0Schristos 				 */
102168dbbb44Schristos 		}
102268dbbb44Schristos 
1023ccc794f0Schristos 		if (n <= 0) {
1024abb0f93cSkardel 			/*
1025abb0f93cSkardel 			 * Timed out.  Return what we have
1026abb0f93cSkardel 			 */
1027abb0f93cSkardel 			if (numfrags == 0) {
1028abb0f93cSkardel 				if (timeo)
10293123f114Skardel 					fprintf(stderr,
1030abb0f93cSkardel 						"%s: timed out, nothing received\n",
1031abb0f93cSkardel 						currenthost);
1032abb0f93cSkardel 				return ERR_TIMEOUT;
10333123f114Skardel 			}
1034abb0f93cSkardel 			if (timeo)
10353123f114Skardel 				fprintf(stderr,
1036abb0f93cSkardel 					"%s: timed out with incomplete data\n",
1037abb0f93cSkardel 					currenthost);
1038abb0f93cSkardel 			if (debug) {
10393123f114Skardel 				fprintf(stderr,
10403123f114Skardel 					"ERR_INCOMPLETE: Received fragments:\n");
10413123f114Skardel 				for (f = 0; f < numfrags; f++)
10423123f114Skardel 					fprintf(stderr,
10432950cc38Schristos 						"%2u: %5d %5d\t%3d octets\n",
10442950cc38Schristos 						(u_int)f, offsets[f],
10453123f114Skardel 						offsets[f] +
10463123f114Skardel 						counts[f],
10473123f114Skardel 						counts[f]);
10483123f114Skardel 				fprintf(stderr,
10493123f114Skardel 					"last fragment %sreceived\n",
10503123f114Skardel 					(seenlastfrag)
10513123f114Skardel 					    ? ""
10523123f114Skardel 					    : "not ");
1053abb0f93cSkardel 			}
1054abb0f93cSkardel 			return ERR_INCOMPLETE;
1055abb0f93cSkardel 		}
1056abb0f93cSkardel 
1057abb0f93cSkardel 		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
1058ccc794f0Schristos 		if (n < 0) {
1059058f310fSchristos 			warning("read");
1060abb0f93cSkardel 			return -1;
1061abb0f93cSkardel 		}
1062abb0f93cSkardel 
1063abb0f93cSkardel 		if (debug >= 4) {
10642950cc38Schristos 			printf("Response packet:\n");
10652950cc38Schristos 			dump_hex_printable(&rpkt, n);
1066abb0f93cSkardel 		}
1067abb0f93cSkardel 
1068abb0f93cSkardel 		/*
1069abb0f93cSkardel 		 * Check for format errors.  Bug proofing.
1070abb0f93cSkardel 		 */
1071a2545411Skardel 		if (n < (int)CTL_HEADER_LEN) {
1072abb0f93cSkardel 			if (debug)
1073abb0f93cSkardel 				printf("Short (%d byte) packet received\n", n);
1074abb0f93cSkardel 			continue;
1075abb0f93cSkardel 		}
1076abb0f93cSkardel 		if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
1077abb0f93cSkardel 		    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
1078abb0f93cSkardel 			if (debug)
1079abb0f93cSkardel 				printf("Packet received with version %d\n",
1080abb0f93cSkardel 				       PKT_VERSION(rpkt.li_vn_mode));
1081abb0f93cSkardel 			continue;
1082abb0f93cSkardel 		}
1083abb0f93cSkardel 		if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
1084abb0f93cSkardel 			if (debug)
1085abb0f93cSkardel 				printf("Packet received with mode %d\n",
1086abb0f93cSkardel 				       PKT_MODE(rpkt.li_vn_mode));
1087abb0f93cSkardel 			continue;
1088abb0f93cSkardel 		}
1089abb0f93cSkardel 		if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
1090abb0f93cSkardel 			if (debug)
1091abb0f93cSkardel 				printf("Received request packet, wanted response\n");
1092abb0f93cSkardel 			continue;
1093abb0f93cSkardel 		}
1094abb0f93cSkardel 
1095abb0f93cSkardel 		/*
1096abb0f93cSkardel 		 * Check opcode and sequence number for a match.
1097abb0f93cSkardel 		 * Could be old data getting to us.
1098abb0f93cSkardel 		 */
1099abb0f93cSkardel 		if (ntohs(rpkt.sequence) != sequence) {
1100abb0f93cSkardel 			if (debug)
11013123f114Skardel 				printf("Received sequnce number %d, wanted %d\n",
1102abb0f93cSkardel 				       ntohs(rpkt.sequence), sequence);
1103abb0f93cSkardel 			continue;
1104abb0f93cSkardel 		}
1105abb0f93cSkardel 		if (CTL_OP(rpkt.r_m_e_op) != opcode) {
1106abb0f93cSkardel 			if (debug)
1107abb0f93cSkardel 			    printf(
1108abb0f93cSkardel 				    "Received opcode %d, wanted %d (sequence number okay)\n",
1109abb0f93cSkardel 				    CTL_OP(rpkt.r_m_e_op), opcode);
1110abb0f93cSkardel 			continue;
1111abb0f93cSkardel 		}
1112abb0f93cSkardel 
1113abb0f93cSkardel 		/*
1114abb0f93cSkardel 		 * Check the error code.  If non-zero, return it.
1115abb0f93cSkardel 		 */
1116abb0f93cSkardel 		if (CTL_ISERROR(rpkt.r_m_e_op)) {
1117abb0f93cSkardel 			errcode = (ntohs(rpkt.status) >> 8) & 0xff;
11182950cc38Schristos 			if (CTL_ISMORE(rpkt.r_m_e_op))
11192950cc38Schristos 				TRACE(1, ("Error code %d received on not-final packet\n",
11202950cc38Schristos 					  errcode));
1121abb0f93cSkardel 			if (errcode == CERR_UNSPEC)
1122abb0f93cSkardel 				return ERR_UNSPEC;
1123abb0f93cSkardel 			return errcode;
1124abb0f93cSkardel 		}
1125abb0f93cSkardel 
1126abb0f93cSkardel 		/*
1127abb0f93cSkardel 		 * Check the association ID to make sure it matches what
1128abb0f93cSkardel 		 * we sent.
1129abb0f93cSkardel 		 */
1130abb0f93cSkardel 		if (ntohs(rpkt.associd) != associd) {
11312950cc38Schristos 			TRACE(1, ("Association ID %d doesn't match expected %d\n",
11322950cc38Schristos 				  ntohs(rpkt.associd), associd));
1133abb0f93cSkardel 			/*
1134abb0f93cSkardel 			 * Hack for silly fuzzballs which, at the time of writing,
1135abb0f93cSkardel 			 * return an assID of sys.peer when queried for system variables.
1136abb0f93cSkardel 			 */
1137abb0f93cSkardel #ifdef notdef
1138abb0f93cSkardel 			continue;
1139abb0f93cSkardel #endif
1140abb0f93cSkardel 		}
1141abb0f93cSkardel 
1142abb0f93cSkardel 		/*
1143abb0f93cSkardel 		 * Collect offset and count.  Make sure they make sense.
1144abb0f93cSkardel 		 */
1145abb0f93cSkardel 		offset = ntohs(rpkt.offset);
1146abb0f93cSkardel 		count = ntohs(rpkt.count);
1147abb0f93cSkardel 
1148abb0f93cSkardel 		/*
1149abb0f93cSkardel 		 * validate received payload size is padded to next 32-bit
1150abb0f93cSkardel 		 * boundary and no smaller than claimed by rpkt.count
1151abb0f93cSkardel 		 */
1152abb0f93cSkardel 		if (n & 0x3) {
11532950cc38Schristos 			TRACE(1, ("Response packet not padded, size = %d\n",
11542950cc38Schristos 				  n));
1155abb0f93cSkardel 			continue;
1156abb0f93cSkardel 		}
1157abb0f93cSkardel 
1158abb0f93cSkardel 		shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
1159abb0f93cSkardel 
1160abb0f93cSkardel 		if (n < shouldbesize) {
11612950cc38Schristos 			printf("Response packet claims %u octets payload, above %ld received\n",
1162717847f5Schristos 			       count, (long)(n - CTL_HEADER_LEN));
1163abb0f93cSkardel 			return ERR_INCOMPLETE;
1164abb0f93cSkardel 		}
1165abb0f93cSkardel 
1166abb0f93cSkardel 		if (debug >= 3 && shouldbesize > n) {
1167abb0f93cSkardel 			u_int32 key;
1168abb0f93cSkardel 			u_int32 *lpkt;
1169abb0f93cSkardel 			int maclen;
1170abb0f93cSkardel 
1171abb0f93cSkardel 			/*
1172abb0f93cSkardel 			 * Usually we ignore authentication, but for debugging purposes
1173abb0f93cSkardel 			 * we watch it here.
1174abb0f93cSkardel 			 */
1175abb0f93cSkardel 			/* round to 8 octet boundary */
1176abb0f93cSkardel 			shouldbesize = (shouldbesize + 7) & ~7;
1177abb0f93cSkardel 
1178abb0f93cSkardel 			maclen = n - shouldbesize;
1179e19314b7Schristos 			if (maclen >= (int)MIN_MAC_LEN) {
1180abb0f93cSkardel 				printf(
1181abb0f93cSkardel 					"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1182abb0f93cSkardel 					n, shouldbesize, maclen);
1183abb0f93cSkardel 				lpkt = (u_int32 *)&rpkt;
1184abb0f93cSkardel 				printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1185abb0f93cSkardel 				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1186abb0f93cSkardel 				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1187abb0f93cSkardel 				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1188abb0f93cSkardel 				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1189abb0f93cSkardel 				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1190abb0f93cSkardel 				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1191abb0f93cSkardel 				key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1192abb0f93cSkardel 				printf("Authenticated with keyid %lu\n", (u_long)key);
1193abb0f93cSkardel 				if (key != 0 && key != info_auth_keyid) {
1194abb0f93cSkardel 					printf("We don't know that key\n");
1195abb0f93cSkardel 				} else {
1196abb0f93cSkardel 					if (authdecrypt(key, (u_int32 *)&rpkt,
1197abb0f93cSkardel 					    n - maclen, maclen)) {
1198abb0f93cSkardel 						printf("Auth okay!\n");
1199abb0f93cSkardel 					} else {
1200abb0f93cSkardel 						printf("Auth failed!\n");
1201abb0f93cSkardel 					}
1202abb0f93cSkardel 				}
1203abb0f93cSkardel 			}
1204abb0f93cSkardel 		}
1205abb0f93cSkardel 
12062950cc38Schristos 		TRACE(2, ("Got packet, size = %d\n", n));
12072950cc38Schristos 		if (count > (n - CTL_HEADER_LEN)) {
12082950cc38Schristos 			TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
12092950cc38Schristos 				  count, (long)n - CTL_HEADER_LEN));
1210abb0f93cSkardel 			continue;
1211abb0f93cSkardel 		}
1212abb0f93cSkardel 		if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
12132950cc38Schristos 			TRACE(1, ("Received count of 0 in non-final fragment\n"));
1214abb0f93cSkardel 			continue;
1215abb0f93cSkardel 		}
1216abb0f93cSkardel 		if (offset + count > sizeof(pktdata)) {
12172950cc38Schristos 			TRACE(1, ("Offset %u, count %u, too big for buffer\n",
12182950cc38Schristos 				  offset, count));
1219abb0f93cSkardel 			return ERR_TOOMUCH;
1220abb0f93cSkardel 		}
1221abb0f93cSkardel 		if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
12222950cc38Schristos 			TRACE(1, ("Received second last fragment packet\n"));
1223abb0f93cSkardel 			continue;
1224abb0f93cSkardel 		}
1225abb0f93cSkardel 
1226abb0f93cSkardel 		/*
1227abb0f93cSkardel 		 * So far, so good.  Record this fragment, making sure it doesn't
1228abb0f93cSkardel 		 * overlap anything.
1229abb0f93cSkardel 		 */
12302950cc38Schristos 		TRACE(2, ("Packet okay\n"));
1231abb0f93cSkardel 
1232abb0f93cSkardel 		if (numfrags > (MAXFRAGS - 1)) {
12332950cc38Schristos 			TRACE(2, ("Number of fragments exceeds maximum %d\n",
12342950cc38Schristos 				  MAXFRAGS - 1));
1235abb0f93cSkardel 			return ERR_TOOMUCH;
1236abb0f93cSkardel 		}
1237abb0f93cSkardel 
1238abb0f93cSkardel 		/*
1239abb0f93cSkardel 		 * Find the position for the fragment relative to any
1240abb0f93cSkardel 		 * previously received.
1241abb0f93cSkardel 		 */
12423123f114Skardel 		for (f = 0;
12433123f114Skardel 		     f < numfrags && offsets[f] < offset;
12443123f114Skardel 		     f++) {
1245abb0f93cSkardel 			/* empty body */ ;
1246abb0f93cSkardel 		}
1247abb0f93cSkardel 
12483123f114Skardel 		if (f < numfrags && offset == offsets[f]) {
12492950cc38Schristos 			TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
12502950cc38Schristos 				  count, offset, counts[f], offsets[f]));
1251abb0f93cSkardel 			continue;
1252abb0f93cSkardel 		}
1253abb0f93cSkardel 
12543123f114Skardel 		if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
12552950cc38Schristos 			TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
12562950cc38Schristos 				  offset, counts[f-1], offsets[f-1]));
1257abb0f93cSkardel 			continue;
1258abb0f93cSkardel 		}
1259abb0f93cSkardel 
12603123f114Skardel 		if (f < numfrags && (offset + count) > offsets[f]) {
12612950cc38Schristos 			TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
12622950cc38Schristos 				  count, offset, offsets[f]));
1263abb0f93cSkardel 			continue;
1264abb0f93cSkardel 		}
1265abb0f93cSkardel 
12663123f114Skardel 		for (ff = numfrags; ff > f; ff--) {
12673123f114Skardel 			offsets[ff] = offsets[ff-1];
12683123f114Skardel 			counts[ff] = counts[ff-1];
1269abb0f93cSkardel 		}
12703123f114Skardel 		offsets[f] = offset;
12713123f114Skardel 		counts[f] = count;
1272abb0f93cSkardel 		numfrags++;
1273abb0f93cSkardel 
1274abb0f93cSkardel 		/*
1275abb0f93cSkardel 		 * Got that stuffed in right.  Figure out if this was the last.
1276abb0f93cSkardel 		 * Record status info out of the last packet.
1277abb0f93cSkardel 		 */
1278abb0f93cSkardel 		if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1279abb0f93cSkardel 			seenlastfrag = 1;
1280abb0f93cSkardel 			if (rstatus != 0)
1281abb0f93cSkardel 				*rstatus = ntohs(rpkt.status);
1282abb0f93cSkardel 		}
1283abb0f93cSkardel 
1284abb0f93cSkardel 		/*
128568dbbb44Schristos 		 * Copy the data into the data buffer, and bump the
128668dbbb44Schristos 		 * timout base in case we need more.
1287abb0f93cSkardel 		 */
12882950cc38Schristos 		memcpy((char *)pktdata + offset, &rpkt.u, count);
128968dbbb44Schristos 		tobase = (uint32_t)time(NULL);
1290abb0f93cSkardel 
1291abb0f93cSkardel 		/*
1292abb0f93cSkardel 		 * If we've seen the last fragment, look for holes in the sequence.
1293abb0f93cSkardel 		 * If there aren't any, we're done.
1294abb0f93cSkardel 		 */
1295717847f5Schristos #if !defined(SYS_WINNT) && defined(EINTR)
129668dbbb44Schristos 		maybe_final:
1297717847f5Schristos #endif
1298717847f5Schristos 
1299abb0f93cSkardel 		if (seenlastfrag && offsets[0] == 0) {
13003123f114Skardel 			for (f = 1; f < numfrags; f++)
13013123f114Skardel 				if (offsets[f-1] + counts[f-1] !=
13023123f114Skardel 				    offsets[f])
1303abb0f93cSkardel 					break;
13043123f114Skardel 			if (f == numfrags) {
13053123f114Skardel 				*rsize = offsets[f-1] + counts[f-1];
13062950cc38Schristos 				TRACE(1, ("%lu packets reassembled into response\n",
13072950cc38Schristos 					  (u_long)numfrags));
1308abb0f93cSkardel 				return 0;
1309abb0f93cSkardel 			}
1310abb0f93cSkardel 		}
1311abb0f93cSkardel 	}  /* giant for (;;) collecting response packets */
1312abb0f93cSkardel }  /* getresponse() */
1313abb0f93cSkardel 
1314abb0f93cSkardel 
1315abb0f93cSkardel /*
1316abb0f93cSkardel  * sendrequest - format and send a request packet
1317abb0f93cSkardel  */
1318abb0f93cSkardel static int
1319abb0f93cSkardel sendrequest(
1320abb0f93cSkardel 	int opcode,
13212950cc38Schristos 	associd_t associd,
1322abb0f93cSkardel 	int auth,
13238b8da087Schristos 	size_t qsize,
13242950cc38Schristos 	const char *qdata
1325abb0f93cSkardel 	)
1326abb0f93cSkardel {
1327abb0f93cSkardel 	struct ntp_control qpkt;
13288b8da087Schristos 	size_t	pktsize;
1329abb0f93cSkardel 	u_long	key_id;
1330abb0f93cSkardel 	char *	pass;
13318b8da087Schristos 	size_t	maclen;
1332abb0f93cSkardel 
1333abb0f93cSkardel 	/*
1334abb0f93cSkardel 	 * Check to make sure the data will fit in one packet
1335abb0f93cSkardel 	 */
1336abb0f93cSkardel 	if (qsize > CTL_MAX_DATA_LEN) {
1337abb0f93cSkardel 		fprintf(stderr,
13388b8da087Schristos 			"***Internal error!  qsize (%zu) too large\n",
1339abb0f93cSkardel 			qsize);
1340abb0f93cSkardel 		return 1;
1341abb0f93cSkardel 	}
1342abb0f93cSkardel 
1343abb0f93cSkardel 	/*
1344abb0f93cSkardel 	 * Fill in the packet
1345abb0f93cSkardel 	 */
1346abb0f93cSkardel 	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1347abb0f93cSkardel 	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
1348abb0f93cSkardel 	qpkt.sequence = htons(sequence);
1349abb0f93cSkardel 	qpkt.status = 0;
1350abb0f93cSkardel 	qpkt.associd = htons((u_short)associd);
1351abb0f93cSkardel 	qpkt.offset = 0;
1352abb0f93cSkardel 	qpkt.count = htons((u_short)qsize);
1353abb0f93cSkardel 
1354abb0f93cSkardel 	pktsize = CTL_HEADER_LEN;
1355abb0f93cSkardel 
1356abb0f93cSkardel 	/*
1357abb0f93cSkardel 	 * If we have data, copy and pad it out to a 32-bit boundary.
1358abb0f93cSkardel 	 */
1359abb0f93cSkardel 	if (qsize > 0) {
13602950cc38Schristos 		memcpy(&qpkt.u, qdata, (size_t)qsize);
1361abb0f93cSkardel 		pktsize += qsize;
1362abb0f93cSkardel 		while (pktsize & (sizeof(u_int32) - 1)) {
13632950cc38Schristos 			qpkt.u.data[qsize++] = 0;
1364abb0f93cSkardel 			pktsize++;
1365abb0f93cSkardel 		}
1366abb0f93cSkardel 	}
1367abb0f93cSkardel 
1368abb0f93cSkardel 	/*
1369abb0f93cSkardel 	 * If it isn't authenticated we can just send it.  Otherwise
1370abb0f93cSkardel 	 * we're going to have to think about it a little.
1371abb0f93cSkardel 	 */
1372abb0f93cSkardel 	if (!auth && !always_auth) {
1373abb0f93cSkardel 		return sendpkt(&qpkt, pktsize);
1374abb0f93cSkardel 	}
1375abb0f93cSkardel 
1376abb0f93cSkardel 	/*
1377abb0f93cSkardel 	 * Pad out packet to a multiple of 8 octets to be sure
1378abb0f93cSkardel 	 * receiver can handle it.
1379abb0f93cSkardel 	 */
1380abb0f93cSkardel 	while (pktsize & 7) {
13812950cc38Schristos 		qpkt.u.data[qsize++] = 0;
1382abb0f93cSkardel 		pktsize++;
1383abb0f93cSkardel 	}
1384abb0f93cSkardel 
1385abb0f93cSkardel 	/*
1386abb0f93cSkardel 	 * Get the keyid and the password if we don't have one.
1387abb0f93cSkardel 	 */
1388abb0f93cSkardel 	if (info_auth_keyid == 0) {
1389abb0f93cSkardel 		key_id = getkeyid("Keyid: ");
1390abb0f93cSkardel 		if (key_id == 0 || key_id > NTP_MAXKEY) {
1391abb0f93cSkardel 			fprintf(stderr,
1392abb0f93cSkardel 				"Invalid key identifier\n");
1393abb0f93cSkardel 			return 1;
1394abb0f93cSkardel 		}
1395abb0f93cSkardel 		info_auth_keyid = key_id;
1396abb0f93cSkardel 	}
1397abb0f93cSkardel 	if (!authistrusted(info_auth_keyid)) {
13983123f114Skardel 		pass = getpass_keytype(info_auth_keytype);
1399abb0f93cSkardel 		if ('\0' == pass[0]) {
1400abb0f93cSkardel 			fprintf(stderr, "Invalid password\n");
1401abb0f93cSkardel 			return 1;
1402abb0f93cSkardel 		}
1403abb0f93cSkardel 		authusekey(info_auth_keyid, info_auth_keytype,
1404abb0f93cSkardel 			   (u_char *)pass);
1405abb0f93cSkardel 		authtrust(info_auth_keyid, 1);
1406abb0f93cSkardel 	}
1407abb0f93cSkardel 
1408abb0f93cSkardel 	/*
1409abb0f93cSkardel 	 * Do the encryption.
1410abb0f93cSkardel 	 */
1411abb0f93cSkardel 	maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1412abb0f93cSkardel 	if (!maclen) {
1413abb0f93cSkardel 		fprintf(stderr, "Key not found\n");
1414abb0f93cSkardel 		return 1;
1415abb0f93cSkardel 	} else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1416abb0f93cSkardel 		fprintf(stderr,
14178b8da087Schristos 			"%zu octet MAC, %zu expected with %zu octet digest\n",
1418abb0f93cSkardel 			maclen, (info_auth_hashlen + sizeof(keyid_t)),
1419abb0f93cSkardel 			info_auth_hashlen);
1420abb0f93cSkardel 		return 1;
1421abb0f93cSkardel 	}
1422abb0f93cSkardel 
1423abb0f93cSkardel 	return sendpkt((char *)&qpkt, pktsize + maclen);
1424abb0f93cSkardel }
1425abb0f93cSkardel 
1426abb0f93cSkardel 
1427abb0f93cSkardel /*
14283123f114Skardel  * show_error_msg - display the error text for a mode 6 error response.
14293123f114Skardel  */
14303123f114Skardel void
14313123f114Skardel show_error_msg(
14323123f114Skardel 	int		m6resp,
14333123f114Skardel 	associd_t	associd
14343123f114Skardel 	)
14353123f114Skardel {
14363123f114Skardel 	if (numhosts > 1)
14373123f114Skardel 		fprintf(stderr, "server=%s ", currenthost);
14383123f114Skardel 
14393123f114Skardel 	switch (m6resp) {
14403123f114Skardel 
14413123f114Skardel 	case CERR_BADFMT:
14423123f114Skardel 		fprintf(stderr,
14433123f114Skardel 		    "***Server reports a bad format request packet\n");
14443123f114Skardel 		break;
14453123f114Skardel 
14463123f114Skardel 	case CERR_PERMISSION:
14473123f114Skardel 		fprintf(stderr,
14483123f114Skardel 		    "***Server disallowed request (authentication?)\n");
14493123f114Skardel 		break;
14503123f114Skardel 
14513123f114Skardel 	case CERR_BADOP:
14523123f114Skardel 		fprintf(stderr,
14533123f114Skardel 		    "***Server reports a bad opcode in request\n");
14543123f114Skardel 		break;
14553123f114Skardel 
14563123f114Skardel 	case CERR_BADASSOC:
14573123f114Skardel 		fprintf(stderr,
14583123f114Skardel 		    "***Association ID %d unknown to server\n",
14593123f114Skardel 		    associd);
14603123f114Skardel 		break;
14613123f114Skardel 
14623123f114Skardel 	case CERR_UNKNOWNVAR:
14633123f114Skardel 		fprintf(stderr,
14643123f114Skardel 		    "***A request variable unknown to the server\n");
14653123f114Skardel 		break;
14663123f114Skardel 
14673123f114Skardel 	case CERR_BADVALUE:
14683123f114Skardel 		fprintf(stderr,
14693123f114Skardel 		    "***Server indicates a request variable was bad\n");
14703123f114Skardel 		break;
14713123f114Skardel 
14723123f114Skardel 	case ERR_UNSPEC:
14733123f114Skardel 		fprintf(stderr,
14743123f114Skardel 		    "***Server returned an unspecified error\n");
14753123f114Skardel 		break;
14763123f114Skardel 
14773123f114Skardel 	case ERR_TIMEOUT:
14783123f114Skardel 		fprintf(stderr, "***Request timed out\n");
14793123f114Skardel 		break;
14803123f114Skardel 
14813123f114Skardel 	case ERR_INCOMPLETE:
14823123f114Skardel 		fprintf(stderr,
14833123f114Skardel 		    "***Response from server was incomplete\n");
14843123f114Skardel 		break;
14853123f114Skardel 
14863123f114Skardel 	case ERR_TOOMUCH:
14873123f114Skardel 		fprintf(stderr,
14883123f114Skardel 		    "***Buffer size exceeded for returned data\n");
14893123f114Skardel 		break;
14903123f114Skardel 
14913123f114Skardel 	default:
14923123f114Skardel 		fprintf(stderr,
14933123f114Skardel 		    "***Server returns unknown error code %d\n",
14943123f114Skardel 		    m6resp);
14953123f114Skardel 	}
14963123f114Skardel }
14973123f114Skardel 
14983123f114Skardel /*
14993123f114Skardel  * doquery - send a request and process the response, displaying
15003123f114Skardel  *	     error messages for any error responses.
1501abb0f93cSkardel  */
1502abb0f93cSkardel int
1503abb0f93cSkardel doquery(
1504abb0f93cSkardel 	int opcode,
15053123f114Skardel 	associd_t associd,
1506abb0f93cSkardel 	int auth,
15078b8da087Schristos 	size_t qsize,
15082950cc38Schristos 	const char *qdata,
1509abb0f93cSkardel 	u_short *rstatus,
15108b8da087Schristos 	size_t *rsize,
15113123f114Skardel 	const char **rdata
15123123f114Skardel 	)
15133123f114Skardel {
15143123f114Skardel 	return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
15153123f114Skardel 			 rsize, rdata, FALSE);
15163123f114Skardel }
15173123f114Skardel 
15183123f114Skardel 
15193123f114Skardel /*
15203123f114Skardel  * doqueryex - send a request and process the response, optionally
15213123f114Skardel  *	       displaying error messages for any error responses.
15223123f114Skardel  */
15233123f114Skardel int
15243123f114Skardel doqueryex(
15253123f114Skardel 	int opcode,
15263123f114Skardel 	associd_t associd,
15273123f114Skardel 	int auth,
15288b8da087Schristos 	size_t qsize,
15292950cc38Schristos 	const char *qdata,
15303123f114Skardel 	u_short *rstatus,
15318b8da087Schristos 	size_t *rsize,
15323123f114Skardel 	const char **rdata,
15333123f114Skardel 	int quiet
1534abb0f93cSkardel 	)
1535abb0f93cSkardel {
1536abb0f93cSkardel 	int res;
1537abb0f93cSkardel 	int done;
1538abb0f93cSkardel 
1539abb0f93cSkardel 	/*
1540abb0f93cSkardel 	 * Check to make sure host is open
1541abb0f93cSkardel 	 */
1542abb0f93cSkardel 	if (!havehost) {
15433123f114Skardel 		fprintf(stderr, "***No host open, use `host' command\n");
1544abb0f93cSkardel 		return -1;
1545abb0f93cSkardel 	}
1546abb0f93cSkardel 
1547abb0f93cSkardel 	done = 0;
1548abb0f93cSkardel 	sequence++;
1549abb0f93cSkardel 
1550abb0f93cSkardel     again:
1551abb0f93cSkardel 	/*
1552abb0f93cSkardel 	 * send a request
1553abb0f93cSkardel 	 */
1554abb0f93cSkardel 	res = sendrequest(opcode, associd, auth, qsize, qdata);
1555abb0f93cSkardel 	if (res != 0)
1556abb0f93cSkardel 		return res;
1557abb0f93cSkardel 
1558abb0f93cSkardel 	/*
1559abb0f93cSkardel 	 * Get the response.  If we got a standard error, print a message
1560abb0f93cSkardel 	 */
1561abb0f93cSkardel 	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1562abb0f93cSkardel 
1563abb0f93cSkardel 	if (res > 0) {
1564abb0f93cSkardel 		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1565abb0f93cSkardel 			if (res == ERR_INCOMPLETE) {
1566abb0f93cSkardel 				/*
1567abb0f93cSkardel 				 * better bump the sequence so we don't
1568abb0f93cSkardel 				 * get confused about differing fragments.
1569abb0f93cSkardel 				 */
1570abb0f93cSkardel 				sequence++;
1571abb0f93cSkardel 			}
1572abb0f93cSkardel 			done = 1;
1573abb0f93cSkardel 			goto again;
1574abb0f93cSkardel 		}
15753123f114Skardel 		if (!quiet)
15763123f114Skardel 			show_error_msg(res, associd);
15773123f114Skardel 
1578abb0f93cSkardel 	}
1579abb0f93cSkardel 	return res;
1580abb0f93cSkardel }
1581abb0f93cSkardel 
1582abb0f93cSkardel 
1583abb0f93cSkardel #ifndef BUILD_AS_LIB
1584abb0f93cSkardel /*
1585abb0f93cSkardel  * getcmds - read commands from the standard input and execute them
1586abb0f93cSkardel  */
1587abb0f93cSkardel static void
1588abb0f93cSkardel getcmds(void)
1589abb0f93cSkardel {
1590abb0f93cSkardel 	char *	line;
1591abb0f93cSkardel 	int	count;
1592abb0f93cSkardel 
1593abb0f93cSkardel 	ntp_readline_init(interactive ? prompt : NULL);
1594abb0f93cSkardel 
1595abb0f93cSkardel 	for (;;) {
1596abb0f93cSkardel 		line = ntp_readline(&count);
1597abb0f93cSkardel 		if (NULL == line)
1598abb0f93cSkardel 			break;
1599abb0f93cSkardel 		docmd(line);
1600abb0f93cSkardel 		free(line);
1601abb0f93cSkardel 	}
1602abb0f93cSkardel 
1603abb0f93cSkardel 	ntp_readline_uninit();
1604abb0f93cSkardel }
1605abb0f93cSkardel #endif /* !BUILD_AS_LIB */
1606abb0f93cSkardel 
1607abb0f93cSkardel 
1608abb0f93cSkardel #if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
1609abb0f93cSkardel /*
1610abb0f93cSkardel  * abortcmd - catch interrupts and abort the current command
1611abb0f93cSkardel  */
16128b8da087Schristos static int
16138b8da087Schristos abortcmd(void)
1614abb0f93cSkardel {
1615abb0f93cSkardel 	if (current_output == stdout)
1616abb0f93cSkardel 		(void) fflush(stdout);
1617abb0f93cSkardel 	putc('\n', stderr);
1618abb0f93cSkardel 	(void) fflush(stderr);
16198b8da087Schristos 	if (jump) {
16208b8da087Schristos 		jump = 0;
162179045f13Schristos 		LONGJMP(interrupt_buf, 1);
16228b8da087Schristos 	}
16238b8da087Schristos 	return TRUE;
1624abb0f93cSkardel }
1625abb0f93cSkardel #endif	/* !SYS_WINNT && !BUILD_AS_LIB */
1626abb0f93cSkardel 
1627abb0f93cSkardel 
1628abb0f93cSkardel #ifndef	BUILD_AS_LIB
1629abb0f93cSkardel /*
1630abb0f93cSkardel  * docmd - decode the command line and execute a command
1631abb0f93cSkardel  */
1632abb0f93cSkardel static void
1633abb0f93cSkardel docmd(
1634abb0f93cSkardel 	const char *cmdline
1635abb0f93cSkardel 	)
1636abb0f93cSkardel {
1637abb0f93cSkardel 	char *tokens[1+MAXARGS+2];
1638abb0f93cSkardel 	struct parse pcmd;
1639abb0f93cSkardel 	int ntok;
1640abb0f93cSkardel 	static int i;
1641abb0f93cSkardel 	struct xcmd *xcmd;
1642*eabc0478Schristos 	int executeonly = 0;
1643abb0f93cSkardel 
1644abb0f93cSkardel 	/*
1645abb0f93cSkardel 	 * Tokenize the command line.  If nothing on it, return.
1646abb0f93cSkardel 	 */
1647abb0f93cSkardel 	tokenize(cmdline, tokens, &ntok);
1648abb0f93cSkardel 	if (ntok == 0)
1649abb0f93cSkardel 	    return;
1650abb0f93cSkardel 
1651abb0f93cSkardel 	/*
1652*eabc0478Schristos 	 * If command prefixed by '~', then quiet output
1653*eabc0478Schristos 	 */
1654*eabc0478Schristos 	if (*tokens[0] == '~') {
1655*eabc0478Schristos 		executeonly++;
1656*eabc0478Schristos 		tokens[0]++;
1657*eabc0478Schristos 	}
1658*eabc0478Schristos 
1659*eabc0478Schristos 	/*
1660abb0f93cSkardel 	 * Find the appropriate command description.
1661abb0f93cSkardel 	 */
1662abb0f93cSkardel 	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1663abb0f93cSkardel 	if (i == 0) {
1664abb0f93cSkardel 		(void) fprintf(stderr, "***Command `%s' unknown\n",
1665abb0f93cSkardel 			       tokens[0]);
1666abb0f93cSkardel 		return;
1667abb0f93cSkardel 	} else if (i >= 2) {
1668abb0f93cSkardel 		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
1669abb0f93cSkardel 			       tokens[0]);
1670abb0f93cSkardel 		return;
1671abb0f93cSkardel 	}
1672abb0f93cSkardel 
16732950cc38Schristos 	/* Warn about ignored extra args */
16742950cc38Schristos 	for (i = MAXARGS + 1; i < ntok ; ++i) {
16752950cc38Schristos 		fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
16762950cc38Schristos 	}
16772950cc38Schristos 
1678abb0f93cSkardel 	/*
1679abb0f93cSkardel 	 * Save the keyword, then walk through the arguments, interpreting
1680abb0f93cSkardel 	 * as we go.
1681abb0f93cSkardel 	 */
1682abb0f93cSkardel 	pcmd.keyword = tokens[0];
1683abb0f93cSkardel 	pcmd.nargs = 0;
1684abb0f93cSkardel 	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1685abb0f93cSkardel 		if ((i+1) >= ntok) {
1686abb0f93cSkardel 			if (!(xcmd->arg[i] & OPT)) {
1687abb0f93cSkardel 				printusage(xcmd, stderr);
1688abb0f93cSkardel 				return;
1689abb0f93cSkardel 			}
1690abb0f93cSkardel 			break;
1691abb0f93cSkardel 		}
1692abb0f93cSkardel 		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1693abb0f93cSkardel 			break;
1694abb0f93cSkardel 		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1695abb0f93cSkardel 			return;
1696abb0f93cSkardel 		pcmd.nargs++;
1697abb0f93cSkardel 	}
1698abb0f93cSkardel 
1699abb0f93cSkardel 	i++;
1700abb0f93cSkardel 	if (i < ntok && *tokens[i] == '>') {
1701abb0f93cSkardel 		char *fname;
1702abb0f93cSkardel 
1703abb0f93cSkardel 		if (*(tokens[i]+1) != '\0')
1704abb0f93cSkardel 			fname = tokens[i]+1;
1705abb0f93cSkardel 		else if ((i+1) < ntok)
1706abb0f93cSkardel 			fname = tokens[i+1];
1707abb0f93cSkardel 		else {
1708abb0f93cSkardel 			(void) fprintf(stderr, "***No file for redirect\n");
1709abb0f93cSkardel 			return;
1710abb0f93cSkardel 		}
1711abb0f93cSkardel 
1712abb0f93cSkardel 		current_output = fopen(fname, "w");
1713abb0f93cSkardel 		if (current_output == NULL) {
1714abb0f93cSkardel 			(void) fprintf(stderr, "***Error opening %s: ", fname);
1715abb0f93cSkardel 			perror("");
1716abb0f93cSkardel 			return;
1717abb0f93cSkardel 		}
1718*eabc0478Schristos 	} else if (executeonly) {		/* Redirect all output to null */
1719*eabc0478Schristos 		current_output = fopen(PATH_DEVNULL, "w");
1720*eabc0478Schristos 		if (current_output == NULL) {
1721*eabc0478Schristos 			(void) fprintf(stderr, "***Error redirecting output to /dev/null: ");
1722*eabc0478Schristos 			perror("");
1723*eabc0478Schristos 			return;
1724*eabc0478Schristos 		}
1725abb0f93cSkardel 	} else {
1726abb0f93cSkardel 		current_output = stdout;
1727abb0f93cSkardel 	}
1728abb0f93cSkardel 
172979045f13Schristos 	if (interactive) {
173079045f13Schristos 		if ( ! SETJMP(interrupt_buf)) {
173179045f13Schristos 			jump = 1;
1732abb0f93cSkardel 			(xcmd->handler)(&pcmd, current_output);
173379045f13Schristos 			jump = 0;
173479045f13Schristos 		} else {
173579045f13Schristos 			fflush(current_output);
173679045f13Schristos 			fputs("\n >>> command aborted <<<\n", stderr);
173779045f13Schristos 			fflush(stderr);
1738abb0f93cSkardel 		}
17392950cc38Schristos 
174079045f13Schristos 	} else {
174179045f13Schristos 		jump = 0;
174279045f13Schristos 		(xcmd->handler)(&pcmd, current_output);
174379045f13Schristos 	}
174479045f13Schristos 	if ((NULL != current_output) && (stdout != current_output)) {
174579045f13Schristos 		(void)fclose(current_output);
174679045f13Schristos 		current_output = NULL;
174779045f13Schristos 	}
1748abb0f93cSkardel }
1749abb0f93cSkardel 
1750abb0f93cSkardel 
1751abb0f93cSkardel /*
1752abb0f93cSkardel  * tokenize - turn a command line into tokens
1753abb0f93cSkardel  *
1754abb0f93cSkardel  * SK: Modified to allow a quoted string
1755abb0f93cSkardel  *
1756abb0f93cSkardel  * HMS: If the first character of the first token is a ':' then (after
1757abb0f93cSkardel  * eating inter-token whitespace) the 2nd token is the rest of the line.
1758abb0f93cSkardel  */
1759abb0f93cSkardel 
1760abb0f93cSkardel static void
1761abb0f93cSkardel tokenize(
1762abb0f93cSkardel 	const char *line,
1763abb0f93cSkardel 	char **tokens,
1764abb0f93cSkardel 	int *ntok
1765abb0f93cSkardel 	)
1766abb0f93cSkardel {
1767abb0f93cSkardel 	register const char *cp;
1768abb0f93cSkardel 	register char *sp;
1769abb0f93cSkardel 	static char tspace[MAXLINE];
1770abb0f93cSkardel 
1771abb0f93cSkardel 	sp = tspace;
1772abb0f93cSkardel 	cp = line;
1773abb0f93cSkardel 	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1774abb0f93cSkardel 		tokens[*ntok] = sp;
1775abb0f93cSkardel 
1776abb0f93cSkardel 		/* Skip inter-token whitespace */
1777abb0f93cSkardel 		while (ISSPACE(*cp))
1778abb0f93cSkardel 		    cp++;
1779abb0f93cSkardel 
1780abb0f93cSkardel 		/* If we're at EOL we're done */
1781abb0f93cSkardel 		if (ISEOL(*cp))
1782abb0f93cSkardel 		    break;
1783abb0f93cSkardel 
1784abb0f93cSkardel 		/* If this is the 2nd token and the first token begins
1785abb0f93cSkardel 		 * with a ':', then just grab to EOL.
1786abb0f93cSkardel 		 */
1787abb0f93cSkardel 
1788abb0f93cSkardel 		if (*ntok == 1 && tokens[0][0] == ':') {
1789abb0f93cSkardel 			do {
1790ea66d795Schristos 				if (sp - tspace >= MAXLINE)
1791ea66d795Schristos 					goto toobig;
1792abb0f93cSkardel 				*sp++ = *cp++;
1793abb0f93cSkardel 			} while (!ISEOL(*cp));
1794abb0f93cSkardel 		}
1795abb0f93cSkardel 
1796abb0f93cSkardel 		/* Check if this token begins with a double quote.
1797abb0f93cSkardel 		 * If yes, continue reading till the next double quote
1798abb0f93cSkardel 		 */
1799abb0f93cSkardel 		else if (*cp == '\"') {
1800abb0f93cSkardel 			++cp;
1801abb0f93cSkardel 			do {
1802ea66d795Schristos 				if (sp - tspace >= MAXLINE)
1803ea66d795Schristos 					goto toobig;
1804abb0f93cSkardel 				*sp++ = *cp++;
1805abb0f93cSkardel 			} while ((*cp != '\"') && !ISEOL(*cp));
1806abb0f93cSkardel 			/* HMS: a missing closing " should be an error */
1807abb0f93cSkardel 		}
1808abb0f93cSkardel 		else {
1809abb0f93cSkardel 			do {
1810ea66d795Schristos 				if (sp - tspace >= MAXLINE)
1811ea66d795Schristos 					goto toobig;
1812abb0f93cSkardel 				*sp++ = *cp++;
1813abb0f93cSkardel 			} while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1814abb0f93cSkardel 			/* HMS: Why check for a " in the previous line? */
1815abb0f93cSkardel 		}
1816abb0f93cSkardel 
1817ea66d795Schristos 		if (sp - tspace >= MAXLINE)
1818ea66d795Schristos 			goto toobig;
1819abb0f93cSkardel 		*sp++ = '\0';
1820abb0f93cSkardel 	}
1821ea66d795Schristos 	return;
1822ea66d795Schristos 
1823ea66d795Schristos   toobig:
1824ea66d795Schristos 	*ntok = 0;
1825ea66d795Schristos 	fprintf(stderr,
1826ea66d795Schristos 		"***Line `%s' is too big\n",
1827ea66d795Schristos 		line);
1828ea66d795Schristos 	return;
1829abb0f93cSkardel }
1830abb0f93cSkardel 
1831abb0f93cSkardel 
1832abb0f93cSkardel /*
1833abb0f93cSkardel  * getarg - interpret an argument token
1834abb0f93cSkardel  */
1835abb0f93cSkardel static int
1836abb0f93cSkardel getarg(
18372950cc38Schristos 	const char *str,
1838abb0f93cSkardel 	int code,
1839abb0f93cSkardel 	arg_v *argp
1840abb0f93cSkardel 	)
1841abb0f93cSkardel {
18422950cc38Schristos 	u_long ul;
1843abb0f93cSkardel 
1844abb0f93cSkardel 	switch (code & ~OPT) {
1845abb0f93cSkardel 	case NTP_STR:
1846abb0f93cSkardel 		argp->string = str;
1847abb0f93cSkardel 		break;
18482950cc38Schristos 
1849abb0f93cSkardel 	case NTP_ADD:
18502950cc38Schristos 		if (!getnetnum(str, &argp->netnum, NULL, 0))
1851abb0f93cSkardel 			return 0;
1852abb0f93cSkardel 		break;
18532950cc38Schristos 
1854abb0f93cSkardel 	case NTP_UINT:
18552950cc38Schristos 		if ('&' == str[0]) {
18562950cc38Schristos 			if (!atouint(&str[1], &ul)) {
18572950cc38Schristos 				fprintf(stderr,
18582950cc38Schristos 					"***Association index `%s' invalid/undecodable\n",
18592950cc38Schristos 					str);
1860abb0f93cSkardel 				return 0;
1861abb0f93cSkardel 			}
18622950cc38Schristos 			if (0 == numassoc) {
18632950cc38Schristos 				dogetassoc(stdout);
18642950cc38Schristos 				if (0 == numassoc) {
18652950cc38Schristos 					fprintf(stderr,
18662950cc38Schristos 						"***No associations found, `%s' unknown\n",
18672950cc38Schristos 						str);
1868abb0f93cSkardel 					return 0;
1869abb0f93cSkardel 				}
1870abb0f93cSkardel 			}
18712950cc38Schristos 			ul = min(ul, numassoc);
18722950cc38Schristos 			argp->uval = assoc_cache[ul - 1].assid;
1873abb0f93cSkardel 			break;
1874abb0f93cSkardel 		}
18752950cc38Schristos 		if (!atouint(str, &argp->uval)) {
18762950cc38Schristos 			fprintf(stderr, "***Illegal unsigned value %s\n",
18772950cc38Schristos 				str);
1878abb0f93cSkardel 			return 0;
1879abb0f93cSkardel 		}
1880abb0f93cSkardel 		break;
18812950cc38Schristos 
18822950cc38Schristos 	case NTP_INT:
18832950cc38Schristos 		if (!atoint(str, &argp->ival)) {
18842950cc38Schristos 			fprintf(stderr, "***Illegal integer value %s\n",
18852950cc38Schristos 				str);
18862950cc38Schristos 			return 0;
18872950cc38Schristos 		}
18882950cc38Schristos 		break;
18892950cc38Schristos 
1890abb0f93cSkardel 	case IP_VERSION:
18912950cc38Schristos 		if (!strcmp("-6", str)) {
1892abb0f93cSkardel 			argp->ival = 6;
18932950cc38Schristos 		} else if (!strcmp("-4", str)) {
1894abb0f93cSkardel 			argp->ival = 4;
18952950cc38Schristos 		} else {
18962950cc38Schristos 			fprintf(stderr, "***Version must be either 4 or 6\n");
1897abb0f93cSkardel 			return 0;
1898abb0f93cSkardel 		}
1899abb0f93cSkardel 		break;
1900abb0f93cSkardel 	}
1901abb0f93cSkardel 
1902abb0f93cSkardel 	return 1;
1903abb0f93cSkardel }
1904abb0f93cSkardel #endif	/* !BUILD_AS_LIB */
1905abb0f93cSkardel 
1906abb0f93cSkardel 
1907abb0f93cSkardel /*
1908abb0f93cSkardel  * findcmd - find a command in a command description table
1909abb0f93cSkardel  */
1910abb0f93cSkardel static int
1911abb0f93cSkardel findcmd(
19122950cc38Schristos 	const char *	str,
1913abb0f93cSkardel 	struct xcmd *	clist1,
1914abb0f93cSkardel 	struct xcmd *	clist2,
1915abb0f93cSkardel 	struct xcmd **	cmd
1916abb0f93cSkardel 	)
1917abb0f93cSkardel {
19182950cc38Schristos 	struct xcmd *cl;
19198b8da087Schristos 	size_t clen;
1920abb0f93cSkardel 	int nmatch;
1921abb0f93cSkardel 	struct xcmd *nearmatch = NULL;
1922abb0f93cSkardel 	struct xcmd *clist;
1923abb0f93cSkardel 
1924abb0f93cSkardel 	clen = strlen(str);
1925abb0f93cSkardel 	nmatch = 0;
1926abb0f93cSkardel 	if (clist1 != 0)
1927abb0f93cSkardel 	    clist = clist1;
1928abb0f93cSkardel 	else if (clist2 != 0)
1929abb0f93cSkardel 	    clist = clist2;
1930abb0f93cSkardel 	else
1931abb0f93cSkardel 	    return 0;
1932abb0f93cSkardel 
1933abb0f93cSkardel     again:
1934abb0f93cSkardel 	for (cl = clist; cl->keyword != 0; cl++) {
1935abb0f93cSkardel 		/* do a first character check, for efficiency */
1936abb0f93cSkardel 		if (*str != *(cl->keyword))
1937abb0f93cSkardel 		    continue;
1938abb0f93cSkardel 		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1939abb0f93cSkardel 			/*
1940abb0f93cSkardel 			 * Could be extact match, could be approximate.
1941abb0f93cSkardel 			 * Is exact if the length of the keyword is the
1942abb0f93cSkardel 			 * same as the str.
1943abb0f93cSkardel 			 */
1944abb0f93cSkardel 			if (*((cl->keyword) + clen) == '\0') {
1945abb0f93cSkardel 				*cmd = cl;
1946abb0f93cSkardel 				return 1;
1947abb0f93cSkardel 			}
1948abb0f93cSkardel 			nmatch++;
1949abb0f93cSkardel 			nearmatch = cl;
1950abb0f93cSkardel 		}
1951abb0f93cSkardel 	}
1952abb0f93cSkardel 
1953abb0f93cSkardel 	/*
1954abb0f93cSkardel 	 * See if there is more to do.  If so, go again.  Sorry about the
1955abb0f93cSkardel 	 * goto, too much looking at BSD sources...
1956abb0f93cSkardel 	 */
1957abb0f93cSkardel 	if (clist == clist1 && clist2 != 0) {
1958abb0f93cSkardel 		clist = clist2;
1959abb0f93cSkardel 		goto again;
1960abb0f93cSkardel 	}
1961abb0f93cSkardel 
1962abb0f93cSkardel 	/*
1963abb0f93cSkardel 	 * If we got extactly 1 near match, use it, else return number
1964abb0f93cSkardel 	 * of matches.
1965abb0f93cSkardel 	 */
1966abb0f93cSkardel 	if (nmatch == 1) {
1967abb0f93cSkardel 		*cmd = nearmatch;
1968abb0f93cSkardel 		return 1;
1969abb0f93cSkardel 	}
1970abb0f93cSkardel 	return nmatch;
1971abb0f93cSkardel }
1972abb0f93cSkardel 
1973abb0f93cSkardel 
1974abb0f93cSkardel /*
1975abb0f93cSkardel  * getnetnum - given a host name, return its net number
1976abb0f93cSkardel  *	       and (optional) full name
1977abb0f93cSkardel  */
1978abb0f93cSkardel int
1979abb0f93cSkardel getnetnum(
1980abb0f93cSkardel 	const char *hname,
1981abb0f93cSkardel 	sockaddr_u *num,
1982abb0f93cSkardel 	char *fullhost,
1983abb0f93cSkardel 	int af
1984abb0f93cSkardel 	)
1985abb0f93cSkardel {
1986abb0f93cSkardel 	struct addrinfo hints, *ai = NULL;
1987abb0f93cSkardel 
19883123f114Skardel 	ZERO(hints);
1989abb0f93cSkardel 	hints.ai_flags = AI_CANONNAME;
1990abb0f93cSkardel #ifdef AI_ADDRCONFIG
1991abb0f93cSkardel 	hints.ai_flags |= AI_ADDRCONFIG;
1992abb0f93cSkardel #endif
1993abb0f93cSkardel 
19943123f114Skardel 	/*
19953123f114Skardel 	 * decodenetnum only works with addresses, but handles syntax
19963123f114Skardel 	 * that getaddrinfo doesn't:  [2001::1]:1234
19973123f114Skardel 	 */
1998abb0f93cSkardel 	if (decodenetnum(hname, num)) {
19993123f114Skardel 		if (fullhost != NULL)
20003123f114Skardel 			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
20013123f114Skardel 				    LENHOSTNAME, NULL, 0, 0);
2002abb0f93cSkardel 		return 1;
2003abb0f93cSkardel 	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
20042950cc38Schristos 		INSIST(sizeof(*num) >= ai->ai_addrlen);
20053123f114Skardel 		memcpy(num, ai->ai_addr, ai->ai_addrlen);
20063123f114Skardel 		if (fullhost != NULL) {
20072950cc38Schristos 			if (ai->ai_canonname != NULL)
20082950cc38Schristos 				strlcpy(fullhost, ai->ai_canonname,
20093123f114Skardel 					LENHOSTNAME);
20102950cc38Schristos 			else
20113123f114Skardel 				getnameinfo(&num->sa, SOCKLEN(num),
20123123f114Skardel 					    fullhost, LENHOSTNAME, NULL,
20133123f114Skardel 					    0, 0);
2014abb0f93cSkardel 		}
20152950cc38Schristos 		freeaddrinfo(ai);
20163123f114Skardel 		return 1;
20173123f114Skardel 	}
20183123f114Skardel 	fprintf(stderr, "***Can't find host %s\n", hname);
20193123f114Skardel 
20203123f114Skardel 	return 0;
2021abb0f93cSkardel }
2022abb0f93cSkardel 
20232950cc38Schristos 
2024abb0f93cSkardel /*
2025abb0f93cSkardel  * nntohost - convert network number to host name.  This routine enforces
2026abb0f93cSkardel  *	       the showhostnames setting.
2027abb0f93cSkardel  */
20283123f114Skardel const char *
2029abb0f93cSkardel nntohost(
2030abb0f93cSkardel 	sockaddr_u *netnum
2031abb0f93cSkardel 	)
2032abb0f93cSkardel {
20333123f114Skardel 	return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
20343123f114Skardel }
20353123f114Skardel 
20363123f114Skardel 
20373123f114Skardel /*
20383123f114Skardel  * nntohost_col - convert network number to host name in fixed width.
20393123f114Skardel  *		  This routine enforces the showhostnames setting.
20403123f114Skardel  *		  When displaying hostnames longer than the width,
20413123f114Skardel  *		  the first part of the hostname is displayed.  When
20423123f114Skardel  *		  displaying numeric addresses longer than the width,
20433123f114Skardel  *		  Such as IPv6 addresses, the caller decides whether
20443123f114Skardel  *		  the first or last of the numeric address is used.
20453123f114Skardel  */
20463123f114Skardel const char *
20473123f114Skardel nntohost_col(
20483123f114Skardel 	sockaddr_u *	addr,
20493123f114Skardel 	size_t		width,
20503123f114Skardel 	int		preserve_lowaddrbits
20513123f114Skardel 	)
20523123f114Skardel {
20533123f114Skardel 	const char *	out;
20543123f114Skardel 
20552950cc38Schristos 	if (!showhostnames || SOCK_UNSPEC(addr)) {
20563123f114Skardel 		if (preserve_lowaddrbits)
20573123f114Skardel 			out = trunc_left(stoa(addr), width);
2058abb0f93cSkardel 		else
20593123f114Skardel 			out = trunc_right(stoa(addr), width);
20603123f114Skardel 	} else if (ISREFCLOCKADR(addr)) {
20613123f114Skardel 		out = refnumtoa(addr);
20623123f114Skardel 	} else {
20633123f114Skardel 		out = trunc_right(socktohost(addr), width);
20643123f114Skardel 	}
20653123f114Skardel 	return out;
2066abb0f93cSkardel }
2067abb0f93cSkardel 
2068abb0f93cSkardel 
2069abb0f93cSkardel /*
20702950cc38Schristos  * nntohostp() is the same as nntohost() plus a :port suffix
20712950cc38Schristos  */
20722950cc38Schristos const char *
20732950cc38Schristos nntohostp(
20742950cc38Schristos 	sockaddr_u *netnum
20752950cc38Schristos 	)
20762950cc38Schristos {
20772950cc38Schristos 	const char *	hostn;
20782950cc38Schristos 	char *		buf;
20792950cc38Schristos 
20802950cc38Schristos 	if (!showhostnames || SOCK_UNSPEC(netnum))
20812950cc38Schristos 		return sptoa(netnum);
20822950cc38Schristos 	else if (ISREFCLOCKADR(netnum))
20832950cc38Schristos 		return refnumtoa(netnum);
20842950cc38Schristos 
20852950cc38Schristos 	hostn = socktohost(netnum);
20862950cc38Schristos 	LIB_GETBUF(buf);
20872950cc38Schristos 	snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
20882950cc38Schristos 
20892950cc38Schristos 	return buf;
20902950cc38Schristos }
20912950cc38Schristos 
20922950cc38Schristos /*
2093abb0f93cSkardel  * rtdatetolfp - decode an RT-11 date into an l_fp
2094abb0f93cSkardel  */
2095abb0f93cSkardel static int
2096abb0f93cSkardel rtdatetolfp(
2097abb0f93cSkardel 	char *str,
2098abb0f93cSkardel 	l_fp *lfp
2099abb0f93cSkardel 	)
2100abb0f93cSkardel {
2101abb0f93cSkardel 	register char *cp;
2102abb0f93cSkardel 	register int i;
2103abb0f93cSkardel 	struct calendar cal;
2104abb0f93cSkardel 	char buf[4];
2105abb0f93cSkardel 
2106abb0f93cSkardel 	cal.yearday = 0;
2107abb0f93cSkardel 
2108abb0f93cSkardel 	/*
2109abb0f93cSkardel 	 * An RT-11 date looks like:
2110abb0f93cSkardel 	 *
2111abb0f93cSkardel 	 * d[d]-Mth-y[y] hh:mm:ss
2112abb0f93cSkardel 	 *
2113abb0f93cSkardel 	 * (No docs, but assume 4-digit years are also legal...)
2114abb0f93cSkardel 	 *
2115abb0f93cSkardel 	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
2116abb0f93cSkardel 	 */
2117abb0f93cSkardel 	cp = str;
21184eea345dSchristos 	if (!isdigit(pgetc(cp))) {
2119abb0f93cSkardel 		if (*cp == '-') {
2120abb0f93cSkardel 			/*
2121abb0f93cSkardel 			 * Catch special case
2122abb0f93cSkardel 			 */
2123abb0f93cSkardel 			L_CLR(lfp);
2124abb0f93cSkardel 			return 1;
2125abb0f93cSkardel 		}
2126abb0f93cSkardel 		return 0;
2127abb0f93cSkardel 	}
2128abb0f93cSkardel 
2129abb0f93cSkardel 	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
21304eea345dSchristos 	if (isdigit(pgetc(cp))) {
2131abb0f93cSkardel 		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
2132abb0f93cSkardel 		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
2133abb0f93cSkardel 	}
2134abb0f93cSkardel 
2135abb0f93cSkardel 	if (*cp++ != '-')
2136abb0f93cSkardel 	    return 0;
2137abb0f93cSkardel 
2138abb0f93cSkardel 	for (i = 0; i < 3; i++)
2139abb0f93cSkardel 	    buf[i] = *cp++;
2140abb0f93cSkardel 	buf[3] = '\0';
2141abb0f93cSkardel 
2142abb0f93cSkardel 	for (i = 0; i < 12; i++)
2143abb0f93cSkardel 	    if (STREQ(buf, months[i]))
2144abb0f93cSkardel 		break;
2145abb0f93cSkardel 	if (i == 12)
2146abb0f93cSkardel 	    return 0;
2147abb0f93cSkardel 	cal.month = (u_char)(i + 1);
2148abb0f93cSkardel 
2149abb0f93cSkardel 	if (*cp++ != '-')
2150abb0f93cSkardel 	    return 0;
2151abb0f93cSkardel 
21524eea345dSchristos 	if (!isdigit(pgetc(cp)))
2153abb0f93cSkardel 	    return 0;
2154abb0f93cSkardel 	cal.year = (u_short)(*cp++ - '0');
21554eea345dSchristos 	if (isdigit(pgetc(cp))) {
2156abb0f93cSkardel 		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2157abb0f93cSkardel 		cal.year = (u_short)(*cp++ - '0');
2158abb0f93cSkardel 	}
21594eea345dSchristos 	if (isdigit(pgetc(cp))) {
2160abb0f93cSkardel 		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2161abb0f93cSkardel 		cal.year = (u_short)(cal.year + *cp++ - '0');
2162abb0f93cSkardel 	}
21634eea345dSchristos 	if (isdigit(pgetc(cp))) {
2164abb0f93cSkardel 		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2165abb0f93cSkardel 		cal.year = (u_short)(cal.year + *cp++ - '0');
2166abb0f93cSkardel 	}
2167abb0f93cSkardel 
2168abb0f93cSkardel 	/*
2169abb0f93cSkardel 	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
2170abb0f93cSkardel 	 */
2171abb0f93cSkardel 	if (cal.year == 0) {
2172abb0f93cSkardel 		L_CLR(lfp);
2173abb0f93cSkardel 		return 1;
2174abb0f93cSkardel 	}
2175abb0f93cSkardel 
21764eea345dSchristos 	if (*cp++ != ' ' || !isdigit(pgetc(cp)))
2177abb0f93cSkardel 	    return 0;
2178abb0f93cSkardel 	cal.hour = (u_char)(*cp++ - '0');
21794eea345dSchristos 	if (isdigit(pgetc(cp))) {
2180abb0f93cSkardel 		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
2181abb0f93cSkardel 		cal.hour = (u_char)(cal.hour + *cp++ - '0');
2182abb0f93cSkardel 	}
2183abb0f93cSkardel 
21844eea345dSchristos 	if (*cp++ != ':' || !isdigit(pgetc(cp)))
2185abb0f93cSkardel 	    return 0;
2186abb0f93cSkardel 	cal.minute = (u_char)(*cp++ - '0');
21874eea345dSchristos 	if (isdigit(pgetc(cp))) {
2188abb0f93cSkardel 		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
2189abb0f93cSkardel 		cal.minute = (u_char)(cal.minute + *cp++ - '0');
2190abb0f93cSkardel 	}
2191abb0f93cSkardel 
21924eea345dSchristos 	if (*cp++ != ':' || !isdigit(pgetc(cp)))
2193abb0f93cSkardel 	    return 0;
2194abb0f93cSkardel 	cal.second = (u_char)(*cp++ - '0');
21954eea345dSchristos 	if (isdigit(pgetc(cp))) {
2196abb0f93cSkardel 		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
2197abb0f93cSkardel 		cal.second = (u_char)(cal.second + *cp++ - '0');
2198abb0f93cSkardel 	}
2199abb0f93cSkardel 
2200abb0f93cSkardel 	/*
2201abb0f93cSkardel 	 * For RT-11, 1972 seems to be the pivot year
2202abb0f93cSkardel 	 */
2203abb0f93cSkardel 	if (cal.year < 72)
2204abb0f93cSkardel 		cal.year += 2000;
2205abb0f93cSkardel 	if (cal.year < 100)
2206abb0f93cSkardel 		cal.year += 1900;
2207abb0f93cSkardel 
2208*eabc0478Schristos 	/* check for complaints from 'caltontp()'! */
2209abb0f93cSkardel 	lfp->l_uf = 0;
2210*eabc0478Schristos 	errno = 0;
2211*eabc0478Schristos 	lfp->l_ui = caltontp(&cal);
2212*eabc0478Schristos 	return (errno == 0);
2213abb0f93cSkardel }
2214abb0f93cSkardel 
2215abb0f93cSkardel 
2216abb0f93cSkardel /*
2217abb0f93cSkardel  * decodets - decode a timestamp into an l_fp format number, with
2218abb0f93cSkardel  *	      consideration of fuzzball formats.
2219abb0f93cSkardel  */
2220abb0f93cSkardel int
2221abb0f93cSkardel decodets(
2222abb0f93cSkardel 	char *str,
2223abb0f93cSkardel 	l_fp *lfp
2224abb0f93cSkardel 	)
2225abb0f93cSkardel {
22263123f114Skardel 	char *cp;
22273123f114Skardel 	char buf[30];
22283123f114Skardel 	size_t b;
22293123f114Skardel 
2230abb0f93cSkardel 	/*
2231abb0f93cSkardel 	 * If it starts with a 0x, decode as hex.
2232abb0f93cSkardel 	 */
2233abb0f93cSkardel 	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2234abb0f93cSkardel 		return hextolfp(str+2, lfp);
2235abb0f93cSkardel 
2236abb0f93cSkardel 	/*
2237abb0f93cSkardel 	 * If it starts with a '"', try it as an RT-11 date.
2238abb0f93cSkardel 	 */
2239abb0f93cSkardel 	if (*str == '"') {
22403123f114Skardel 		cp = str + 1;
22413123f114Skardel 		b = 0;
22423123f114Skardel 		while ('"' != *cp && '\0' != *cp &&
22433123f114Skardel 		       b < COUNTOF(buf) - 1)
22443123f114Skardel 			buf[b++] = *cp++;
22453123f114Skardel 		buf[b] = '\0';
2246abb0f93cSkardel 		return rtdatetolfp(buf, lfp);
2247abb0f93cSkardel 	}
2248abb0f93cSkardel 
2249abb0f93cSkardel 	/*
2250abb0f93cSkardel 	 * Might still be hex.  Check out the first character.  Talk
2251abb0f93cSkardel 	 * about heuristics!
2252abb0f93cSkardel 	 */
2253abb0f93cSkardel 	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2254abb0f93cSkardel 		return hextolfp(str, lfp);
2255abb0f93cSkardel 
2256abb0f93cSkardel 	/*
2257abb0f93cSkardel 	 * Try it as a decimal.  If this fails, try as an unquoted
2258abb0f93cSkardel 	 * RT-11 date.  This code should go away eventually.
2259abb0f93cSkardel 	 */
2260abb0f93cSkardel 	if (atolfp(str, lfp))
2261abb0f93cSkardel 		return 1;
2262abb0f93cSkardel 
2263abb0f93cSkardel 	return rtdatetolfp(str, lfp);
2264abb0f93cSkardel }
2265abb0f93cSkardel 
2266abb0f93cSkardel 
2267abb0f93cSkardel /*
2268abb0f93cSkardel  * decodetime - decode a time value.  It should be in milliseconds
2269abb0f93cSkardel  */
2270abb0f93cSkardel int
2271abb0f93cSkardel decodetime(
2272abb0f93cSkardel 	char *str,
2273abb0f93cSkardel 	l_fp *lfp
2274abb0f93cSkardel 	)
2275abb0f93cSkardel {
2276abb0f93cSkardel 	return mstolfp(str, lfp);
2277abb0f93cSkardel }
2278abb0f93cSkardel 
2279abb0f93cSkardel 
2280abb0f93cSkardel /*
2281abb0f93cSkardel  * decodeint - decode an integer
2282abb0f93cSkardel  */
2283abb0f93cSkardel int
2284abb0f93cSkardel decodeint(
2285abb0f93cSkardel 	char *str,
2286abb0f93cSkardel 	long *val
2287abb0f93cSkardel 	)
2288abb0f93cSkardel {
2289abb0f93cSkardel 	if (*str == '0') {
2290abb0f93cSkardel 		if (*(str+1) == 'x' || *(str+1) == 'X')
2291abb0f93cSkardel 		    return hextoint(str+2, (u_long *)val);
2292abb0f93cSkardel 		return octtoint(str, (u_long *)val);
2293abb0f93cSkardel 	}
2294abb0f93cSkardel 	return atoint(str, val);
2295abb0f93cSkardel }
2296abb0f93cSkardel 
2297abb0f93cSkardel 
2298abb0f93cSkardel /*
2299abb0f93cSkardel  * decodeuint - decode an unsigned integer
2300abb0f93cSkardel  */
2301abb0f93cSkardel int
2302abb0f93cSkardel decodeuint(
2303abb0f93cSkardel 	char *str,
2304abb0f93cSkardel 	u_long *val
2305abb0f93cSkardel 	)
2306abb0f93cSkardel {
2307abb0f93cSkardel 	if (*str == '0') {
2308abb0f93cSkardel 		if (*(str + 1) == 'x' || *(str + 1) == 'X')
2309abb0f93cSkardel 			return (hextoint(str + 2, val));
2310abb0f93cSkardel 		return (octtoint(str, val));
2311abb0f93cSkardel 	}
2312abb0f93cSkardel 	return (atouint(str, val));
2313abb0f93cSkardel }
2314abb0f93cSkardel 
2315abb0f93cSkardel 
2316abb0f93cSkardel /*
2317abb0f93cSkardel  * decodearr - decode an array of time values
2318abb0f93cSkardel  */
2319abb0f93cSkardel static int
2320abb0f93cSkardel decodearr(
23214eea345dSchristos 	char *cp,
2322abb0f93cSkardel 	int  *narr,
23234eea345dSchristos 	l_fp *lfpa,
23244eea345dSchristos 	int   amax
2325abb0f93cSkardel 	)
2326abb0f93cSkardel {
23274eea345dSchristos 	char *bp;
2328abb0f93cSkardel 	char buf[60];
2329abb0f93cSkardel 
2330abb0f93cSkardel 	*narr = 0;
2331abb0f93cSkardel 
23324eea345dSchristos 	while (*narr < amax && *cp) {
23334eea345dSchristos 		if (isspace(pgetc(cp))) {
23344eea345dSchristos 			do
23354eea345dSchristos 				++cp;
23364eea345dSchristos 			while (*cp && isspace(pgetc(cp)));
23374eea345dSchristos 		} else {
2338abb0f93cSkardel 			bp = buf;
23394eea345dSchristos 			do {
23404eea345dSchristos 				if (bp != (buf + sizeof(buf) - 1))
23414eea345dSchristos 					*bp++ = *cp;
23424eea345dSchristos 				++cp;
23434eea345dSchristos 			} while (*cp && !isspace(pgetc(cp)));
23444eea345dSchristos 			*bp = '\0';
2345abb0f93cSkardel 
23464eea345dSchristos 			if (!decodetime(buf, lfpa))
2347abb0f93cSkardel 				return 0;
23484eea345dSchristos 			++(*narr);
23494eea345dSchristos 			++lfpa;
23504eea345dSchristos 		}
2351abb0f93cSkardel 	}
2352abb0f93cSkardel 	return 1;
2353abb0f93cSkardel }
2354abb0f93cSkardel 
2355abb0f93cSkardel 
2356abb0f93cSkardel /*
2357abb0f93cSkardel  * Finally, the built in command handlers
2358abb0f93cSkardel  */
2359abb0f93cSkardel 
2360abb0f93cSkardel /*
2361abb0f93cSkardel  * help - tell about commands, or details of a particular command
2362abb0f93cSkardel  */
2363abb0f93cSkardel static void
2364abb0f93cSkardel help(
2365abb0f93cSkardel 	struct parse *pcmd,
2366abb0f93cSkardel 	FILE *fp
2367abb0f93cSkardel 	)
2368abb0f93cSkardel {
2369abb0f93cSkardel 	struct xcmd *xcp = NULL;	/* quiet warning */
23702950cc38Schristos 	const char *cmd;
2371abb0f93cSkardel 	const char *list[100];
23723123f114Skardel 	size_t word, words;
23733123f114Skardel 	size_t row, rows;
23743123f114Skardel 	size_t col, cols;
23753123f114Skardel 	size_t length;
2376abb0f93cSkardel 
2377abb0f93cSkardel 	if (pcmd->nargs == 0) {
2378abb0f93cSkardel 		words = 0;
23793123f114Skardel 		for (xcp = builtins; xcp->keyword != NULL; xcp++) {
23802950cc38Schristos 			if (*(xcp->keyword) != '?' &&
23812950cc38Schristos 			    words < COUNTOF(list))
2382abb0f93cSkardel 				list[words++] = xcp->keyword;
2383abb0f93cSkardel 		}
23843123f114Skardel 		for (xcp = opcmds; xcp->keyword != NULL; xcp++)
23852950cc38Schristos 			if (words < COUNTOF(list))
2386abb0f93cSkardel 				list[words++] = xcp->keyword;
2387abb0f93cSkardel 
23882950cc38Schristos 		qsort((void *)list, words, sizeof(list[0]), helpsort);
2389abb0f93cSkardel 		col = 0;
2390abb0f93cSkardel 		for (word = 0; word < words; word++) {
23913123f114Skardel 			length = strlen(list[word]);
23923123f114Skardel 			col = max(col, length);
2393abb0f93cSkardel 		}
2394abb0f93cSkardel 
2395abb0f93cSkardel 		cols = SCREENWIDTH / ++col;
2396abb0f93cSkardel 		rows = (words + cols - 1) / cols;
2397abb0f93cSkardel 
23983123f114Skardel 		fprintf(fp, "ntpq commands:\n");
2399abb0f93cSkardel 
2400abb0f93cSkardel 		for (row = 0; row < rows; row++) {
24013123f114Skardel 			for (word = row; word < words; word += rows)
24022950cc38Schristos 				fprintf(fp, "%-*.*s", (int)col,
24032950cc38Schristos 					(int)col - 1, list[word]);
24043123f114Skardel 			fprintf(fp, "\n");
2405abb0f93cSkardel 		}
2406abb0f93cSkardel 	} else {
2407abb0f93cSkardel 		cmd = pcmd->argval[0].string;
2408abb0f93cSkardel 		words = findcmd(cmd, builtins, opcmds, &xcp);
2409abb0f93cSkardel 		if (words == 0) {
24103123f114Skardel 			fprintf(stderr,
2411abb0f93cSkardel 				"Command `%s' is unknown\n", cmd);
2412abb0f93cSkardel 			return;
2413abb0f93cSkardel 		} else if (words >= 2) {
24143123f114Skardel 			fprintf(stderr,
2415abb0f93cSkardel 				"Command `%s' is ambiguous\n", cmd);
2416abb0f93cSkardel 			return;
2417abb0f93cSkardel 		}
24183123f114Skardel 		fprintf(fp, "function: %s\n", xcp->comment);
2419abb0f93cSkardel 		printusage(xcp, fp);
2420abb0f93cSkardel 	}
2421abb0f93cSkardel }
2422abb0f93cSkardel 
2423abb0f93cSkardel 
2424abb0f93cSkardel /*
2425abb0f93cSkardel  * helpsort - do hostname qsort comparisons
2426abb0f93cSkardel  */
2427abb0f93cSkardel static int
2428abb0f93cSkardel helpsort(
2429abb0f93cSkardel 	const void *t1,
2430abb0f93cSkardel 	const void *t2
2431abb0f93cSkardel 	)
2432abb0f93cSkardel {
24333123f114Skardel 	const char * const *	name1 = t1;
24343123f114Skardel 	const char * const *	name2 = t2;
2435abb0f93cSkardel 
2436abb0f93cSkardel 	return strcmp(*name1, *name2);
2437abb0f93cSkardel }
2438abb0f93cSkardel 
2439abb0f93cSkardel 
2440abb0f93cSkardel /*
2441abb0f93cSkardel  * printusage - print usage information for a command
2442abb0f93cSkardel  */
2443abb0f93cSkardel static void
2444abb0f93cSkardel printusage(
2445abb0f93cSkardel 	struct xcmd *xcp,
2446abb0f93cSkardel 	FILE *fp
2447abb0f93cSkardel 	)
2448abb0f93cSkardel {
2449abb0f93cSkardel 	register int i;
2450abb0f93cSkardel 
24512950cc38Schristos 	/* XXX: Do we need to warn about extra args here too? */
24522950cc38Schristos 
2453abb0f93cSkardel 	(void) fprintf(fp, "usage: %s", xcp->keyword);
2454abb0f93cSkardel 	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2455abb0f93cSkardel 		if (xcp->arg[i] & OPT)
2456abb0f93cSkardel 		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2457abb0f93cSkardel 		else
2458abb0f93cSkardel 		    (void) fprintf(fp, " %s", xcp->desc[i]);
2459abb0f93cSkardel 	}
2460abb0f93cSkardel 	(void) fprintf(fp, "\n");
2461abb0f93cSkardel }
2462abb0f93cSkardel 
2463abb0f93cSkardel 
2464abb0f93cSkardel /*
2465abb0f93cSkardel  * timeout - set time out time
2466abb0f93cSkardel  */
2467abb0f93cSkardel static void
2468abb0f93cSkardel timeout(
2469abb0f93cSkardel 	struct parse *pcmd,
2470abb0f93cSkardel 	FILE *fp
2471abb0f93cSkardel 	)
2472abb0f93cSkardel {
2473abb0f93cSkardel 	int val;
2474abb0f93cSkardel 
2475abb0f93cSkardel 	if (pcmd->nargs == 0) {
2476abb0f93cSkardel 		val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2477abb0f93cSkardel 		(void) fprintf(fp, "primary timeout %d ms\n", val);
2478abb0f93cSkardel 	} else {
2479abb0f93cSkardel 		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2480abb0f93cSkardel 		tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
2481abb0f93cSkardel 			* 1000;
2482abb0f93cSkardel 	}
2483abb0f93cSkardel }
2484abb0f93cSkardel 
2485abb0f93cSkardel 
2486abb0f93cSkardel /*
2487abb0f93cSkardel  * auth_delay - set delay for auth requests
2488abb0f93cSkardel  */
2489abb0f93cSkardel static void
2490abb0f93cSkardel auth_delay(
2491abb0f93cSkardel 	struct parse *pcmd,
2492abb0f93cSkardel 	FILE *fp
2493abb0f93cSkardel 	)
2494abb0f93cSkardel {
2495abb0f93cSkardel 	int isneg;
2496abb0f93cSkardel 	u_long val;
2497abb0f93cSkardel 
2498abb0f93cSkardel 	if (pcmd->nargs == 0) {
2499abb0f93cSkardel 		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2500abb0f93cSkardel 		(void) fprintf(fp, "delay %lu ms\n", val);
2501abb0f93cSkardel 	} else {
2502abb0f93cSkardel 		if (pcmd->argval[0].ival < 0) {
2503abb0f93cSkardel 			isneg = 1;
2504abb0f93cSkardel 			val = (u_long)(-pcmd->argval[0].ival);
2505abb0f93cSkardel 		} else {
2506abb0f93cSkardel 			isneg = 0;
2507abb0f93cSkardel 			val = (u_long)pcmd->argval[0].ival;
2508abb0f93cSkardel 		}
2509abb0f93cSkardel 
2510abb0f93cSkardel 		delay_time.l_ui = val / 1000;
2511abb0f93cSkardel 		val %= 1000;
2512abb0f93cSkardel 		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
2513abb0f93cSkardel 
2514abb0f93cSkardel 		if (isneg)
2515abb0f93cSkardel 		    L_NEG(&delay_time);
2516abb0f93cSkardel 	}
2517abb0f93cSkardel }
2518abb0f93cSkardel 
2519abb0f93cSkardel 
2520abb0f93cSkardel /*
2521abb0f93cSkardel  * host - set the host we are dealing with.
2522abb0f93cSkardel  */
2523abb0f93cSkardel static void
2524abb0f93cSkardel host(
2525abb0f93cSkardel 	struct parse *pcmd,
2526abb0f93cSkardel 	FILE *fp
2527abb0f93cSkardel 	)
2528abb0f93cSkardel {
2529abb0f93cSkardel 	int i;
2530abb0f93cSkardel 
2531abb0f93cSkardel 	if (pcmd->nargs == 0) {
2532abb0f93cSkardel 		if (havehost)
2533abb0f93cSkardel 			(void) fprintf(fp, "current host is %s\n",
2534abb0f93cSkardel 					   currenthost);
2535abb0f93cSkardel 		else
2536abb0f93cSkardel 			(void) fprintf(fp, "no current host\n");
2537abb0f93cSkardel 		return;
2538abb0f93cSkardel 	}
2539abb0f93cSkardel 
2540abb0f93cSkardel 	i = 0;
2541abb0f93cSkardel 	ai_fam_templ = ai_fam_default;
2542abb0f93cSkardel 	if (pcmd->nargs == 2) {
2543abb0f93cSkardel 		if (!strcmp("-4", pcmd->argval[i].string))
2544abb0f93cSkardel 			ai_fam_templ = AF_INET;
2545abb0f93cSkardel 		else if (!strcmp("-6", pcmd->argval[i].string))
2546abb0f93cSkardel 			ai_fam_templ = AF_INET6;
2547abb0f93cSkardel 		else
25482950cc38Schristos 			goto no_change;
2549abb0f93cSkardel 		i = 1;
2550abb0f93cSkardel 	}
25512950cc38Schristos 	if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
25522950cc38Schristos 		fprintf(fp, "current host set to %s\n", currenthost);
2553abb0f93cSkardel 	} else {
25542950cc38Schristos     no_change:
2555abb0f93cSkardel 		if (havehost)
25562950cc38Schristos 			fprintf(fp, "current host remains %s\n",
2557abb0f93cSkardel 				currenthost);
2558abb0f93cSkardel 		else
25592950cc38Schristos 			fprintf(fp, "still no current host\n");
2560abb0f93cSkardel 	}
2561abb0f93cSkardel }
2562abb0f93cSkardel 
2563abb0f93cSkardel 
2564abb0f93cSkardel /*
2565abb0f93cSkardel  * poll - do one (or more) polls of the host via NTP
2566abb0f93cSkardel  */
2567abb0f93cSkardel /*ARGSUSED*/
2568abb0f93cSkardel static void
2569abb0f93cSkardel ntp_poll(
2570abb0f93cSkardel 	struct parse *pcmd,
2571abb0f93cSkardel 	FILE *fp
2572abb0f93cSkardel 	)
2573abb0f93cSkardel {
2574abb0f93cSkardel 	(void) fprintf(fp, "poll not implemented yet\n");
2575abb0f93cSkardel }
2576abb0f93cSkardel 
2577abb0f93cSkardel 
2578abb0f93cSkardel /*
257968dbbb44Schristos  * showdrefid2str - return a string explanation of the value of drefid
258068dbbb44Schristos  */
258168dbbb44Schristos static const char *
258268dbbb44Schristos showdrefid2str(void)
258368dbbb44Schristos {
258468dbbb44Schristos 	switch (drefid) {
258568dbbb44Schristos 	    case REFID_HASH:
258668dbbb44Schristos 	    	return "hash";
258768dbbb44Schristos 	    case REFID_IPV4:
258868dbbb44Schristos 	    	return "ipv4";
258968dbbb44Schristos 	    default:
259068dbbb44Schristos 	    	return "Unknown";
259168dbbb44Schristos 	}
259268dbbb44Schristos }
259368dbbb44Schristos 
259468dbbb44Schristos 
259568dbbb44Schristos /*
259668dbbb44Schristos  * drefid - display/change "display hash"
259768dbbb44Schristos  */
259868dbbb44Schristos static void
259968dbbb44Schristos showdrefid(
260068dbbb44Schristos 	struct parse *pcmd,
260168dbbb44Schristos 	FILE *fp
260268dbbb44Schristos 	)
260368dbbb44Schristos {
260468dbbb44Schristos 	if (pcmd->nargs == 0) {
260568dbbb44Schristos 		(void) fprintf(fp, "drefid value is %s\n", showdrefid2str());
260668dbbb44Schristos 		return;
260768dbbb44Schristos 	} else if (STREQ(pcmd->argval[0].string, "hash")) {
260868dbbb44Schristos 		drefid = REFID_HASH;
260968dbbb44Schristos 	} else if (STREQ(pcmd->argval[0].string, "ipv4")) {
261068dbbb44Schristos 		drefid = REFID_IPV4;
261168dbbb44Schristos 	} else {
261268dbbb44Schristos 		(void) fprintf(fp, "What?\n");
261368dbbb44Schristos 		return;
261468dbbb44Schristos 	}
261568dbbb44Schristos 	(void) fprintf(fp, "drefid value set to %s\n", showdrefid2str());
261668dbbb44Schristos }
261768dbbb44Schristos 
261868dbbb44Schristos 
261968dbbb44Schristos /*
2620abb0f93cSkardel  * keyid - get a keyid to use for authenticating requests
2621abb0f93cSkardel  */
2622abb0f93cSkardel static void
2623abb0f93cSkardel keyid(
2624abb0f93cSkardel 	struct parse *pcmd,
2625abb0f93cSkardel 	FILE *fp
2626abb0f93cSkardel 	)
2627abb0f93cSkardel {
2628abb0f93cSkardel 	if (pcmd->nargs == 0) {
2629abb0f93cSkardel 		if (info_auth_keyid == 0)
2630abb0f93cSkardel 		    (void) fprintf(fp, "no keyid defined\n");
2631abb0f93cSkardel 		else
2632abb0f93cSkardel 		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
2633abb0f93cSkardel 	} else {
2634abb0f93cSkardel 		/* allow zero so that keyid can be cleared. */
2635abb0f93cSkardel 		if(pcmd->argval[0].uval > NTP_MAXKEY)
2636abb0f93cSkardel 		    (void) fprintf(fp, "Invalid key identifier\n");
2637abb0f93cSkardel 		info_auth_keyid = pcmd->argval[0].uval;
2638abb0f93cSkardel 	}
2639abb0f93cSkardel }
2640abb0f93cSkardel 
2641abb0f93cSkardel /*
2642abb0f93cSkardel  * keytype - get type of key to use for authenticating requests
2643abb0f93cSkardel  */
2644abb0f93cSkardel static void
2645abb0f93cSkardel keytype(
2646abb0f93cSkardel 	struct parse *pcmd,
2647abb0f93cSkardel 	FILE *fp
2648abb0f93cSkardel 	)
2649abb0f93cSkardel {
2650abb0f93cSkardel 	const char *	digest_name;
2651abb0f93cSkardel 	size_t		digest_len;
2652abb0f93cSkardel 	int		key_type;
2653abb0f93cSkardel 
2654abb0f93cSkardel 	if (!pcmd->nargs) {
26552f3ccb49Skardel 		fprintf(fp, "keytype is %s with %lu octet digests\n",
2656abb0f93cSkardel 			keytype_name(info_auth_keytype),
26573123f114Skardel 			(u_long)info_auth_hashlen);
2658abb0f93cSkardel 		return;
2659abb0f93cSkardel 	}
2660abb0f93cSkardel 
2661abb0f93cSkardel 	digest_name = pcmd->argval[0].string;
2662abb0f93cSkardel 	digest_len = 0;
2663abb0f93cSkardel 	key_type = keytype_from_text(digest_name, &digest_len);
2664abb0f93cSkardel 
2665abb0f93cSkardel 	if (!key_type) {
26665d681e99Schristos 		fprintf(fp, "keytype is not valid. "
2667abb0f93cSkardel #ifdef OPENSSL
26685d681e99Schristos 			"Type \"help keytype\" for the available digest types.\n");
2669abb0f93cSkardel #else
26705d681e99Schristos 			"Only \"md5\" is available.\n");
2671abb0f93cSkardel #endif
2672abb0f93cSkardel 		return;
2673abb0f93cSkardel 	}
2674abb0f93cSkardel 
2675abb0f93cSkardel 	info_auth_keytype = key_type;
2676abb0f93cSkardel 	info_auth_hashlen = digest_len;
2677abb0f93cSkardel }
2678abb0f93cSkardel 
2679abb0f93cSkardel 
2680abb0f93cSkardel /*
2681abb0f93cSkardel  * passwd - get an authentication key
2682abb0f93cSkardel  */
2683abb0f93cSkardel /*ARGSUSED*/
2684abb0f93cSkardel static void
2685abb0f93cSkardel passwd(
2686abb0f93cSkardel 	struct parse *pcmd,
2687abb0f93cSkardel 	FILE *fp
2688abb0f93cSkardel 	)
2689abb0f93cSkardel {
26902950cc38Schristos 	const char *pass;
2691abb0f93cSkardel 
2692abb0f93cSkardel 	if (info_auth_keyid == 0) {
26932950cc38Schristos 		info_auth_keyid = getkeyid("Keyid: ");
26942950cc38Schristos 		if (info_auth_keyid == 0) {
26952950cc38Schristos 			(void)fprintf(fp, "Keyid must be defined\n");
2696abb0f93cSkardel 			return;
2697abb0f93cSkardel 		}
2698abb0f93cSkardel 	}
26993123f114Skardel 	if (pcmd->nargs >= 1)
27003123f114Skardel 		pass = pcmd->argval[0].string;
2701abb0f93cSkardel 	else {
27023123f114Skardel 		pass = getpass_keytype(info_auth_keytype);
27033123f114Skardel 		if ('\0' == pass[0]) {
27043123f114Skardel 			fprintf(fp, "Password unchanged\n");
27053123f114Skardel 			return;
27063123f114Skardel 		}
27073123f114Skardel 	}
27082950cc38Schristos 	authusekey(info_auth_keyid, info_auth_keytype,
27092950cc38Schristos 		   (const u_char *)pass);
2710abb0f93cSkardel 	authtrust(info_auth_keyid, 1);
2711abb0f93cSkardel }
2712abb0f93cSkardel 
2713abb0f93cSkardel 
2714abb0f93cSkardel /*
2715abb0f93cSkardel  * hostnames - set the showhostnames flag
2716abb0f93cSkardel  */
2717abb0f93cSkardel static void
2718abb0f93cSkardel hostnames(
2719abb0f93cSkardel 	struct parse *pcmd,
2720abb0f93cSkardel 	FILE *fp
2721abb0f93cSkardel 	)
2722abb0f93cSkardel {
2723abb0f93cSkardel 	if (pcmd->nargs == 0) {
2724abb0f93cSkardel 		if (showhostnames)
2725abb0f93cSkardel 		    (void) fprintf(fp, "hostnames being shown\n");
2726abb0f93cSkardel 		else
2727abb0f93cSkardel 		    (void) fprintf(fp, "hostnames not being shown\n");
2728abb0f93cSkardel 	} else {
2729abb0f93cSkardel 		if (STREQ(pcmd->argval[0].string, "yes"))
2730abb0f93cSkardel 		    showhostnames = 1;
2731abb0f93cSkardel 		else if (STREQ(pcmd->argval[0].string, "no"))
2732abb0f93cSkardel 		    showhostnames = 0;
2733abb0f93cSkardel 		else
2734abb0f93cSkardel 		    (void)fprintf(stderr, "What?\n");
2735abb0f93cSkardel 	}
2736abb0f93cSkardel }
2737abb0f93cSkardel 
2738abb0f93cSkardel 
2739abb0f93cSkardel 
2740abb0f93cSkardel /*
2741abb0f93cSkardel  * setdebug - set/change debugging level
2742abb0f93cSkardel  */
2743abb0f93cSkardel static void
2744abb0f93cSkardel setdebug(
2745abb0f93cSkardel 	struct parse *pcmd,
2746abb0f93cSkardel 	FILE *fp
2747abb0f93cSkardel 	)
2748abb0f93cSkardel {
2749abb0f93cSkardel 	if (pcmd->nargs == 0) {
2750abb0f93cSkardel 		(void) fprintf(fp, "debug level is %d\n", debug);
2751abb0f93cSkardel 		return;
2752abb0f93cSkardel 	} else if (STREQ(pcmd->argval[0].string, "no")) {
2753abb0f93cSkardel 		debug = 0;
2754abb0f93cSkardel 	} else if (STREQ(pcmd->argval[0].string, "more")) {
2755abb0f93cSkardel 		debug++;
2756abb0f93cSkardel 	} else if (STREQ(pcmd->argval[0].string, "less")) {
2757abb0f93cSkardel 		debug--;
2758abb0f93cSkardel 	} else {
2759abb0f93cSkardel 		(void) fprintf(fp, "What?\n");
2760abb0f93cSkardel 		return;
2761abb0f93cSkardel 	}
2762abb0f93cSkardel 	(void) fprintf(fp, "debug level set to %d\n", debug);
2763abb0f93cSkardel }
2764abb0f93cSkardel 
2765abb0f93cSkardel 
2766abb0f93cSkardel /*
2767abb0f93cSkardel  * quit - stop this nonsense
2768abb0f93cSkardel  */
2769abb0f93cSkardel /*ARGSUSED*/
2770abb0f93cSkardel static void
2771abb0f93cSkardel quit(
2772abb0f93cSkardel 	struct parse *pcmd,
2773abb0f93cSkardel 	FILE *fp
2774abb0f93cSkardel 	)
2775abb0f93cSkardel {
2776abb0f93cSkardel 	if (havehost)
2777abb0f93cSkardel 	    closesocket(sockfd);	/* cleanliness next to godliness */
2778abb0f93cSkardel 	exit(0);
2779abb0f93cSkardel }
2780abb0f93cSkardel 
2781abb0f93cSkardel 
2782abb0f93cSkardel /*
2783abb0f93cSkardel  * version - print the current version number
2784abb0f93cSkardel  */
2785abb0f93cSkardel /*ARGSUSED*/
2786abb0f93cSkardel static void
2787abb0f93cSkardel version(
2788abb0f93cSkardel 	struct parse *pcmd,
2789abb0f93cSkardel 	FILE *fp
2790abb0f93cSkardel 	)
2791abb0f93cSkardel {
2792abb0f93cSkardel 
2793abb0f93cSkardel 	(void) fprintf(fp, "%s\n", Version);
2794abb0f93cSkardel 	return;
2795abb0f93cSkardel }
2796abb0f93cSkardel 
2797abb0f93cSkardel 
2798abb0f93cSkardel /*
2799abb0f93cSkardel  * raw - set raw mode output
2800abb0f93cSkardel  */
2801abb0f93cSkardel /*ARGSUSED*/
2802abb0f93cSkardel static void
2803abb0f93cSkardel raw(
2804abb0f93cSkardel 	struct parse *pcmd,
2805abb0f93cSkardel 	FILE *fp
2806abb0f93cSkardel 	)
2807abb0f93cSkardel {
2808abb0f93cSkardel 	rawmode = 1;
2809abb0f93cSkardel 	(void) fprintf(fp, "Output set to raw\n");
2810abb0f93cSkardel }
2811abb0f93cSkardel 
2812abb0f93cSkardel 
2813abb0f93cSkardel /*
2814abb0f93cSkardel  * cooked - set cooked mode output
2815abb0f93cSkardel  */
2816abb0f93cSkardel /*ARGSUSED*/
2817abb0f93cSkardel static void
2818abb0f93cSkardel cooked(
2819abb0f93cSkardel 	struct parse *pcmd,
2820abb0f93cSkardel 	FILE *fp
2821abb0f93cSkardel 	)
2822abb0f93cSkardel {
2823abb0f93cSkardel 	rawmode = 0;
2824abb0f93cSkardel 	(void) fprintf(fp, "Output set to cooked\n");
2825abb0f93cSkardel 	return;
2826abb0f93cSkardel }
2827abb0f93cSkardel 
2828abb0f93cSkardel 
2829abb0f93cSkardel /*
2830abb0f93cSkardel  * authenticate - always authenticate requests to this host
2831abb0f93cSkardel  */
2832abb0f93cSkardel static void
2833abb0f93cSkardel authenticate(
2834abb0f93cSkardel 	struct parse *pcmd,
2835abb0f93cSkardel 	FILE *fp
2836abb0f93cSkardel 	)
2837abb0f93cSkardel {
2838abb0f93cSkardel 	if (pcmd->nargs == 0) {
2839abb0f93cSkardel 		if (always_auth) {
2840abb0f93cSkardel 			(void) fprintf(fp,
2841abb0f93cSkardel 				       "authenticated requests being sent\n");
2842abb0f93cSkardel 		} else
2843abb0f93cSkardel 		    (void) fprintf(fp,
2844abb0f93cSkardel 				   "unauthenticated requests being sent\n");
2845abb0f93cSkardel 	} else {
2846abb0f93cSkardel 		if (STREQ(pcmd->argval[0].string, "yes")) {
2847abb0f93cSkardel 			always_auth = 1;
2848abb0f93cSkardel 		} else if (STREQ(pcmd->argval[0].string, "no")) {
2849abb0f93cSkardel 			always_auth = 0;
2850abb0f93cSkardel 		} else
2851abb0f93cSkardel 		    (void)fprintf(stderr, "What?\n");
2852abb0f93cSkardel 	}
2853abb0f93cSkardel }
2854abb0f93cSkardel 
2855abb0f93cSkardel 
2856abb0f93cSkardel /*
2857abb0f93cSkardel  * ntpversion - choose the NTP version to use
2858abb0f93cSkardel  */
2859abb0f93cSkardel static void
2860abb0f93cSkardel ntpversion(
2861abb0f93cSkardel 	struct parse *pcmd,
2862abb0f93cSkardel 	FILE *fp
2863abb0f93cSkardel 	)
2864abb0f93cSkardel {
2865abb0f93cSkardel 	if (pcmd->nargs == 0) {
2866abb0f93cSkardel 		(void) fprintf(fp,
2867abb0f93cSkardel 			       "NTP version being claimed is %d\n", pktversion);
2868abb0f93cSkardel 	} else {
2869abb0f93cSkardel 		if (pcmd->argval[0].uval < NTP_OLDVERSION
2870abb0f93cSkardel 		    || pcmd->argval[0].uval > NTP_VERSION) {
2871abb0f93cSkardel 			(void) fprintf(stderr, "versions %d to %d, please\n",
2872abb0f93cSkardel 				       NTP_OLDVERSION, NTP_VERSION);
2873abb0f93cSkardel 		} else {
2874abb0f93cSkardel 			pktversion = (u_char) pcmd->argval[0].uval;
2875abb0f93cSkardel 		}
2876abb0f93cSkardel 	}
2877abb0f93cSkardel }
2878abb0f93cSkardel 
2879abb0f93cSkardel 
2880058f310fSchristos static void __attribute__((__format__(__printf__, 1, 0)))
2881058f310fSchristos vwarning(const char *fmt, va_list ap)
2882058f310fSchristos {
2883058f310fSchristos 	int serrno = errno;
2884058f310fSchristos 	(void) fprintf(stderr, "%s: ", progname);
2885058f310fSchristos 	vfprintf(stderr, fmt, ap);
28868b8da087Schristos 	(void) fprintf(stderr, ": %s\n", strerror(serrno));
2887058f310fSchristos }
2888058f310fSchristos 
2889abb0f93cSkardel /*
2890abb0f93cSkardel  * warning - print a warning message
2891abb0f93cSkardel  */
2892058f310fSchristos static void __attribute__((__format__(__printf__, 1, 2)))
2893abb0f93cSkardel warning(
2894abb0f93cSkardel 	const char *fmt,
2895058f310fSchristos 	...
2896abb0f93cSkardel 	)
2897abb0f93cSkardel {
2898058f310fSchristos 	va_list ap;
2899058f310fSchristos 	va_start(ap, fmt);
2900058f310fSchristos 	vwarning(fmt, ap);
2901058f310fSchristos 	va_end(ap);
2902abb0f93cSkardel }
2903abb0f93cSkardel 
2904abb0f93cSkardel 
2905abb0f93cSkardel /*
2906abb0f93cSkardel  * error - print a message and exit
2907abb0f93cSkardel  */
2908058f310fSchristos static void __attribute__((__format__(__printf__, 1, 2)))
2909abb0f93cSkardel error(
2910abb0f93cSkardel 	const char *fmt,
2911058f310fSchristos 	...
2912abb0f93cSkardel 	)
2913abb0f93cSkardel {
2914058f310fSchristos 	va_list ap;
2915058f310fSchristos 	va_start(ap, fmt);
2916058f310fSchristos 	vwarning(fmt, ap);
2917058f310fSchristos 	va_end(ap);
2918abb0f93cSkardel 	exit(1);
2919abb0f93cSkardel }
2920abb0f93cSkardel /*
2921abb0f93cSkardel  * getkeyid - prompt the user for a keyid to use
2922abb0f93cSkardel  */
2923abb0f93cSkardel static u_long
2924abb0f93cSkardel getkeyid(
2925abb0f93cSkardel 	const char *keyprompt
2926abb0f93cSkardel 	)
2927abb0f93cSkardel {
29283123f114Skardel 	int c;
2929abb0f93cSkardel 	FILE *fi;
2930abb0f93cSkardel 	char pbuf[20];
29313123f114Skardel 	size_t i;
29323123f114Skardel 	size_t ilim;
2933abb0f93cSkardel 
2934abb0f93cSkardel #ifndef SYS_WINNT
2935abb0f93cSkardel 	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
2936abb0f93cSkardel #else
2937abb0f93cSkardel 	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
2938abb0f93cSkardel #endif /* SYS_WINNT */
2939abb0f93cSkardel 		fi = stdin;
2940abb0f93cSkardel 	else
2941abb0f93cSkardel 		setbuf(fi, (char *)NULL);
2942abb0f93cSkardel 	fprintf(stderr, "%s", keyprompt); fflush(stderr);
29433123f114Skardel 	for (i = 0, ilim = COUNTOF(pbuf) - 1;
29443123f114Skardel 	     i < ilim && (c = getc(fi)) != '\n' && c != EOF;
29453123f114Skardel 	     )
29463123f114Skardel 		pbuf[i++] = (char)c;
29473123f114Skardel 	pbuf[i] = '\0';
2948abb0f93cSkardel 	if (fi != stdin)
2949abb0f93cSkardel 		fclose(fi);
2950abb0f93cSkardel 
2951abb0f93cSkardel 	return (u_long) atoi(pbuf);
2952abb0f93cSkardel }
2953abb0f93cSkardel 
2954abb0f93cSkardel 
2955abb0f93cSkardel /*
2956abb0f93cSkardel  * atoascii - printable-ize possibly ascii data using the character
2957abb0f93cSkardel  *	      transformations cat -v uses.
2958abb0f93cSkardel  */
2959abb0f93cSkardel static void
2960abb0f93cSkardel atoascii(
2961abb0f93cSkardel 	const char *in,
2962abb0f93cSkardel 	size_t in_octets,
2963abb0f93cSkardel 	char *out,
2964abb0f93cSkardel 	size_t out_octets
2965abb0f93cSkardel 	)
2966abb0f93cSkardel {
29672950cc38Schristos 	const u_char *	pchIn;
2968abb0f93cSkardel 	const u_char *	pchInLimit;
29692950cc38Schristos 	u_char *	pchOut;
29702950cc38Schristos 	u_char		c;
2971abb0f93cSkardel 
2972abb0f93cSkardel 	pchIn = (const u_char *)in;
2973abb0f93cSkardel 	pchInLimit = pchIn + in_octets;
2974abb0f93cSkardel 	pchOut = (u_char *)out;
2975abb0f93cSkardel 
2976abb0f93cSkardel 	if (NULL == pchIn) {
2977abb0f93cSkardel 		if (0 < out_octets)
2978abb0f93cSkardel 			*pchOut = '\0';
2979abb0f93cSkardel 		return;
2980abb0f93cSkardel 	}
2981abb0f93cSkardel 
2982abb0f93cSkardel #define	ONEOUT(c)					\
2983abb0f93cSkardel do {							\
2984abb0f93cSkardel 	if (0 == --out_octets) {			\
2985abb0f93cSkardel 		*pchOut = '\0';				\
2986abb0f93cSkardel 		return;					\
2987abb0f93cSkardel 	}						\
2988abb0f93cSkardel 	*pchOut++ = (c);				\
2989abb0f93cSkardel } while (0)
2990abb0f93cSkardel 
2991abb0f93cSkardel 	for (	; pchIn < pchInLimit; pchIn++) {
2992abb0f93cSkardel 		c = *pchIn;
2993abb0f93cSkardel 		if ('\0' == c)
2994abb0f93cSkardel 			break;
2995abb0f93cSkardel 		if (c & 0x80) {
2996abb0f93cSkardel 			ONEOUT('M');
2997abb0f93cSkardel 			ONEOUT('-');
2998abb0f93cSkardel 			c &= 0x7f;
2999abb0f93cSkardel 		}
3000abb0f93cSkardel 		if (c < ' ') {
3001abb0f93cSkardel 			ONEOUT('^');
3002abb0f93cSkardel 			ONEOUT((u_char)(c + '@'));
3003abb0f93cSkardel 		} else if (0x7f == c) {
3004abb0f93cSkardel 			ONEOUT('^');
3005abb0f93cSkardel 			ONEOUT('?');
3006abb0f93cSkardel 		} else
3007abb0f93cSkardel 			ONEOUT(c);
3008abb0f93cSkardel 	}
3009abb0f93cSkardel 	ONEOUT('\0');
3010abb0f93cSkardel 
3011abb0f93cSkardel #undef ONEOUT
3012abb0f93cSkardel }
3013abb0f93cSkardel 
3014abb0f93cSkardel 
3015abb0f93cSkardel /*
3016abb0f93cSkardel  * makeascii - print possibly ascii data using the character
3017abb0f93cSkardel  *	       transformations that cat -v uses.
3018abb0f93cSkardel  */
30193123f114Skardel void
3020abb0f93cSkardel makeascii(
30218b8da087Schristos 	size_t length,
30223123f114Skardel 	const char *data,
3023abb0f93cSkardel 	FILE *fp
3024abb0f93cSkardel 	)
3025abb0f93cSkardel {
30263123f114Skardel 	const u_char *data_u_char;
30273123f114Skardel 	const u_char *cp;
30283123f114Skardel 	int c;
3029abb0f93cSkardel 
30303123f114Skardel 	data_u_char = (const u_char *)data;
30313123f114Skardel 
30323123f114Skardel 	for (cp = data_u_char; cp < data_u_char + length; cp++) {
3033abb0f93cSkardel 		c = (int)*cp;
3034abb0f93cSkardel 		if (c & 0x80) {
3035abb0f93cSkardel 			putc('M', fp);
3036abb0f93cSkardel 			putc('-', fp);
3037abb0f93cSkardel 			c &= 0x7f;
3038abb0f93cSkardel 		}
3039abb0f93cSkardel 
3040abb0f93cSkardel 		if (c < ' ') {
3041abb0f93cSkardel 			putc('^', fp);
3042abb0f93cSkardel 			putc(c + '@', fp);
3043abb0f93cSkardel 		} else if (0x7f == c) {
3044abb0f93cSkardel 			putc('^', fp);
3045abb0f93cSkardel 			putc('?', fp);
3046abb0f93cSkardel 		} else
3047abb0f93cSkardel 			putc(c, fp);
3048abb0f93cSkardel 	}
3049abb0f93cSkardel }
3050abb0f93cSkardel 
3051abb0f93cSkardel 
3052abb0f93cSkardel /*
3053abb0f93cSkardel  * asciize - same thing as makeascii except add a newline
3054abb0f93cSkardel  */
3055abb0f93cSkardel void
3056abb0f93cSkardel asciize(
3057abb0f93cSkardel 	int length,
3058abb0f93cSkardel 	char *data,
3059abb0f93cSkardel 	FILE *fp
3060abb0f93cSkardel 	)
3061abb0f93cSkardel {
3062abb0f93cSkardel 	makeascii(length, data, fp);
3063abb0f93cSkardel 	putc('\n', fp);
3064abb0f93cSkardel }
3065abb0f93cSkardel 
3066abb0f93cSkardel 
3067abb0f93cSkardel /*
30683123f114Skardel  * truncate string to fit clipping excess at end.
30693123f114Skardel  *	"too long"	->	"too l"
30703123f114Skardel  * Used for hostnames.
30713123f114Skardel  */
30723123f114Skardel const char *
30733123f114Skardel trunc_right(
30743123f114Skardel 	const char *	src,
30753123f114Skardel 	size_t		width
30763123f114Skardel 	)
30773123f114Skardel {
30783123f114Skardel 	size_t	sl;
30793123f114Skardel 	char *	out;
30803123f114Skardel 
30813123f114Skardel 
30823123f114Skardel 	sl = strlen(src);
30833123f114Skardel 	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
30843123f114Skardel 		LIB_GETBUF(out);
30853123f114Skardel 		memcpy(out, src, width);
30863123f114Skardel 		out[width] = '\0';
30873123f114Skardel 
30883123f114Skardel 		return out;
30893123f114Skardel 	}
30903123f114Skardel 
30913123f114Skardel 	return src;
30923123f114Skardel }
30933123f114Skardel 
30943123f114Skardel 
30953123f114Skardel /*
30963123f114Skardel  * truncate string to fit by preserving right side and using '_' to hint
30973123f114Skardel  *	"too long"	->	"_long"
30983123f114Skardel  * Used for local IPv6 addresses, where low bits differentiate.
30993123f114Skardel  */
31003123f114Skardel const char *
31013123f114Skardel trunc_left(
31023123f114Skardel 	const char *	src,
31033123f114Skardel 	size_t		width
31043123f114Skardel 	)
31053123f114Skardel {
31063123f114Skardel 	size_t	sl;
31073123f114Skardel 	char *	out;
31083123f114Skardel 
31093123f114Skardel 
31103123f114Skardel 	sl = strlen(src);
31113123f114Skardel 	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
31123123f114Skardel 		LIB_GETBUF(out);
31133123f114Skardel 		out[0] = '_';
31143123f114Skardel 		memcpy(&out[1], &src[sl + 1 - width], width);
31153123f114Skardel 
31163123f114Skardel 		return out;
31173123f114Skardel 	}
31183123f114Skardel 
31193123f114Skardel 	return src;
31203123f114Skardel }
31213123f114Skardel 
31223123f114Skardel 
31233123f114Skardel /*
3124abb0f93cSkardel  * Some circular buffer space
3125abb0f93cSkardel  */
3126abb0f93cSkardel #define	CBLEN	80
3127abb0f93cSkardel #define	NUMCB	6
3128abb0f93cSkardel 
3129abb0f93cSkardel char circ_buf[NUMCB][CBLEN];
3130abb0f93cSkardel int nextcb = 0;
3131abb0f93cSkardel 
313279045f13Schristos /* --------------------------------------------------------------------
313379045f13Schristos  * Parsing a response value list
313479045f13Schristos  *
313579045f13Schristos  * This sounds simple (and it actually is not really hard) but it has
313679045f13Schristos  * some pitfalls.
313779045f13Schristos  *
313879045f13Schristos  * Rule1: CR/LF is never embedded in an item
313979045f13Schristos  * Rule2: An item is a name, optionally followed by a value
314079045f13Schristos  * Rule3: The value is separated from the name by a '='
314179045f13Schristos  * Rule4: Items are separated by a ','
314279045f13Schristos  * Rule5: values can be quoted by '"', in which case they can contain
314379045f13Schristos  *        arbitrary characters but *not* '"', CR and LF
314479045f13Schristos  *
314579045f13Schristos  * There are a few implementations out there that require a somewhat
314679045f13Schristos  * relaxed attitude when parsing a value list, especially since we want
314779045f13Schristos  * to copy names and values into local buffers. If these would overflow,
314879045f13Schristos  * the item should be skipped without terminating the parsing sequence.
314979045f13Schristos  *
315079045f13Schristos  * Also, for empty values, there might be a '=' after the name or not;
315179045f13Schristos  * we treat that equivalent.
315279045f13Schristos  *
315379045f13Schristos  * Parsing an item definitely breaks on a CR/LF. If an item is not
315479045f13Schristos  * followed by a comma (','), parsing stops. In the middle of a quoted
315579045f13Schristos  * character sequence CR/LF terminates the parsing finally without
315679045f13Schristos  * returning a value.
315779045f13Schristos  *
315879045f13Schristos  * White space and other noise is ignored when parsing the data buffer;
315979045f13Schristos  * only CR, LF, ',', '=' and '"' are characters with a special meaning.
316079045f13Schristos  * White space is stripped from the names and values *after* working
316179045f13Schristos  * through the buffer, before making the local copies. If whitespace
316279045f13Schristos  * stripping results in an empty name, parsing resumes.
316379045f13Schristos  */
316479045f13Schristos 
316579045f13Schristos /*
316679045f13Schristos  * nextvar parsing helpers
316779045f13Schristos  */
316879045f13Schristos 
316979045f13Schristos /* predicate: allowed chars inside a quoted string */
317079045f13Schristos static int/*BOOL*/ cp_qschar(int ch)
317179045f13Schristos {
317279045f13Schristos 	return ch && (ch != '"' && ch != '\r' && ch != '\n');
317379045f13Schristos }
317479045f13Schristos 
317579045f13Schristos /* predicate: allowed chars inside an unquoted string */
317679045f13Schristos static int/*BOOL*/ cp_uqchar(int ch)
317779045f13Schristos {
317879045f13Schristos 	return ch && (ch != ',' && ch != '"' && ch != '\r' && ch != '\n');
317979045f13Schristos }
318079045f13Schristos 
318179045f13Schristos /* predicate: allowed chars inside a value name */
318279045f13Schristos static int/*BOOL*/ cp_namechar(int ch)
318379045f13Schristos {
318479045f13Schristos 	return ch && (ch != ',' && ch != '=' && ch != '\r' && ch != '\n');
318579045f13Schristos }
318679045f13Schristos 
318779045f13Schristos /* predicate: characters *between* list items. We're relaxed here. */
318879045f13Schristos static int/*BOOL*/ cp_ivspace(int ch)
318979045f13Schristos {
319079045f13Schristos 	return (ch == ',' || (ch > 0 && ch <= ' '));
319179045f13Schristos }
319279045f13Schristos 
319379045f13Schristos /* get current character (or NUL when on end) */
319479045f13Schristos static inline int
319579045f13Schristos pf_getch(
319679045f13Schristos 	const char **	datap,
319779045f13Schristos 	const char *	endp
319879045f13Schristos 	)
319979045f13Schristos {
320079045f13Schristos 	return (*datap != endp)
320179045f13Schristos 	    ? *(const unsigned char*)*datap
320279045f13Schristos 	    : '\0';
320379045f13Schristos }
320479045f13Schristos 
320579045f13Schristos /* get next character (or NUL when on end) */
320679045f13Schristos static inline int
320779045f13Schristos pf_nextch(
320879045f13Schristos 	const char **	datap,
320979045f13Schristos 	const char *	endp
321079045f13Schristos 	)
321179045f13Schristos {
321279045f13Schristos 	return (*datap != endp && ++(*datap) != endp)
321379045f13Schristos 	    ? *(const unsigned char*)*datap
321479045f13Schristos 	    : '\0';
321579045f13Schristos }
321679045f13Schristos 
321779045f13Schristos static size_t
321879045f13Schristos str_strip(
321979045f13Schristos 	const char ** 	datap,
322079045f13Schristos 	size_t		len
322179045f13Schristos 	)
322279045f13Schristos {
322379045f13Schristos 	static const char empty[] = "";
322479045f13Schristos 
322579045f13Schristos 	if (*datap && len) {
322679045f13Schristos 		const char * cpl = *datap;
322779045f13Schristos 		const char * cpr = cpl + len;
322879045f13Schristos 
322979045f13Schristos 		while (cpl != cpr && *(const unsigned char*)cpl <= ' ')
323079045f13Schristos 			++cpl;
323179045f13Schristos 		while (cpl != cpr && *(const unsigned char*)(cpr - 1) <= ' ')
323279045f13Schristos 			--cpr;
323379045f13Schristos 		*datap = cpl;
323479045f13Schristos 		len = (size_t)(cpr - cpl);
323579045f13Schristos 	} else {
323679045f13Schristos 		*datap = empty;
323779045f13Schristos 		len = 0;
323879045f13Schristos 	}
323979045f13Schristos 	return len;
324079045f13Schristos }
324179045f13Schristos 
324279045f13Schristos static void
324379045f13Schristos pf_error(
324479045f13Schristos 	const char *	what,
324579045f13Schristos 	const char *	where,
324679045f13Schristos 	const char *	whend
324779045f13Schristos 	)
324879045f13Schristos {
324979045f13Schristos #   ifndef BUILD_AS_LIB
325079045f13Schristos 
325179045f13Schristos 	FILE *	ofp = (debug > 0) ? stdout : stderr;
325279045f13Schristos 	size_t	len = (size_t)(whend - where);
325379045f13Schristos 
325479045f13Schristos 	if (len > 50) /* *must* fit into an 'int'! */
325579045f13Schristos 		len = 50;
325679045f13Schristos 	fprintf(ofp, "nextvar: %s: '%.*s'\n",
325779045f13Schristos 		what, (int)len, where);
325879045f13Schristos 
325979045f13Schristos #   else  /*defined(BUILD_AS_LIB)*/
326079045f13Schristos 
326179045f13Schristos 	UNUSED_ARG(what);
326279045f13Schristos 	UNUSED_ARG(where);
326379045f13Schristos 	UNUSED_ARG(whend);
326479045f13Schristos 
326579045f13Schristos #   endif /*defined(BUILD_AS_LIB)*/
326679045f13Schristos }
326779045f13Schristos 
3268abb0f93cSkardel /*
3269abb0f93cSkardel  * nextvar - find the next variable in the buffer
3270abb0f93cSkardel  */
327179045f13Schristos int/*BOOL*/
3272abb0f93cSkardel nextvar(
32738b8da087Schristos 	size_t *datalen,
32743123f114Skardel 	const char **datap,
3275abb0f93cSkardel 	char **vname,
3276abb0f93cSkardel 	char **vvalue
3277abb0f93cSkardel 	)
3278abb0f93cSkardel {
327979045f13Schristos 	enum PState 	{ sDone, sInit, sName, sValU, sValQ };
3280abb0f93cSkardel 
328179045f13Schristos 	static char	name[MAXVARLEN], value[MAXVALLEN];
3282abb0f93cSkardel 
328379045f13Schristos 	const char	*cp, *cpend;
328479045f13Schristos 	const char	*np, *vp;
328579045f13Schristos 	size_t		nlen, vlen;
328679045f13Schristos 	int		ch;
328779045f13Schristos 	enum PState	st;
3288abb0f93cSkardel 
328979045f13Schristos 	cpend = *datap + *datalen;
3290abb0f93cSkardel 
329179045f13Schristos   again:
329279045f13Schristos 	np   = vp   = NULL;
329379045f13Schristos 	nlen = vlen = 0;
3294abb0f93cSkardel 
329579045f13Schristos 	st = sInit;
329679045f13Schristos 	ch = pf_getch(datap, cpend);
329779045f13Schristos 
329879045f13Schristos 	while (st != sDone) {
329979045f13Schristos 		switch (st)
330079045f13Schristos 		{
330179045f13Schristos 		case sInit:	/* handle inter-item chars */
330279045f13Schristos 			while (cp_ivspace(ch))
330379045f13Schristos 				ch = pf_nextch(datap, cpend);
330479045f13Schristos 			if (cp_namechar(ch)) {
330579045f13Schristos 				np = *datap;
330679045f13Schristos 				cp = np;
330779045f13Schristos 				st = sName;
330879045f13Schristos 				ch = pf_nextch(datap, cpend);
33092950cc38Schristos 			} else {
331079045f13Schristos 				goto final_done;
3311abb0f93cSkardel 			}
331279045f13Schristos 			break;
3313abb0f93cSkardel 
331479045f13Schristos 		case sName:	/* collect name */
331579045f13Schristos 			while (cp_namechar(ch))
331679045f13Schristos 				ch = pf_nextch(datap, cpend);
331779045f13Schristos 			nlen = (size_t)(*datap - np);
331879045f13Schristos 			if (ch == '=') {
331979045f13Schristos 				ch = pf_nextch(datap, cpend);
332079045f13Schristos 				vp = *datap;
332179045f13Schristos 				st = sValU;
332279045f13Schristos 			} else {
332379045f13Schristos 				if (ch != ',')
332479045f13Schristos 					*datap = cpend;
332579045f13Schristos 				st = sDone;
332679045f13Schristos 			}
332779045f13Schristos 			break;
332879045f13Schristos 
332979045f13Schristos 		case sValU:	/* collect unquoted part(s) of value */
333079045f13Schristos 			while (cp_uqchar(ch))
333179045f13Schristos 				ch = pf_nextch(datap, cpend);
333279045f13Schristos 			if (ch == '"') {
333379045f13Schristos 				ch = pf_nextch(datap, cpend);
333479045f13Schristos 				st = sValQ;
333579045f13Schristos 			} else {
333679045f13Schristos 				vlen = (size_t)(*datap - vp);
333779045f13Schristos 				if (ch != ',')
333879045f13Schristos 					*datap = cpend;
333979045f13Schristos 				st = sDone;
334079045f13Schristos 			}
334179045f13Schristos 			break;
334279045f13Schristos 
334379045f13Schristos 		case sValQ:	/* collect quoted part(s) of value */
334479045f13Schristos 			while (cp_qschar(ch))
334579045f13Schristos 				ch = pf_nextch(datap, cpend);
334679045f13Schristos 			if (ch == '"') {
334779045f13Schristos 				ch = pf_nextch(datap, cpend);
334879045f13Schristos 				st = sValU;
334979045f13Schristos 			} else {
335079045f13Schristos 				pf_error("no closing quote, stop", cp, cpend);
335179045f13Schristos 				goto final_done;
335279045f13Schristos 			}
335379045f13Schristos 			break;
335479045f13Schristos 
335579045f13Schristos 		default:
335679045f13Schristos 			pf_error("state machine error, stop", *datap, cpend);
335779045f13Schristos 			goto final_done;
335879045f13Schristos 		}
335979045f13Schristos 	}
336079045f13Schristos 
336179045f13Schristos 	/* If name or value do not fit their buffer, croak and start
336279045f13Schristos 	 * over. If there's no name at all after whitespace stripping,
336379045f13Schristos 	 * redo silently.
3364abb0f93cSkardel 	 */
336579045f13Schristos 	nlen = str_strip(&np, nlen);
336679045f13Schristos 	vlen = str_strip(&vp, vlen);
336779045f13Schristos 
336879045f13Schristos 	if (nlen == 0) {
336979045f13Schristos 		goto again;
337079045f13Schristos 	}
337179045f13Schristos 	if (nlen >= sizeof(name)) {
337279045f13Schristos 		pf_error("runaway name", np, cpend);
337379045f13Schristos 		goto again;
337479045f13Schristos 	}
337579045f13Schristos 	if (vlen >= sizeof(value)) {
337679045f13Schristos 		pf_error("runaway value", vp, cpend);
337779045f13Schristos 		goto again;
337879045f13Schristos 	}
337979045f13Schristos 
338079045f13Schristos 	/* copy name and value into NUL-terminated buffers */
338179045f13Schristos 	memcpy(name, np, nlen);
338279045f13Schristos 	name[nlen] = '\0';
338379045f13Schristos 	*vname = name;
338479045f13Schristos 
338579045f13Schristos 	memcpy(value, vp, vlen);
338679045f13Schristos 	value[vlen] = '\0';
3387abb0f93cSkardel 	*vvalue = value;
338879045f13Schristos 
338979045f13Schristos 	/* check if there's more to do or if we are finshed */
339079045f13Schristos 	*datalen = (size_t)(cpend - *datap);
339179045f13Schristos 	return TRUE;
339279045f13Schristos 
339379045f13Schristos   final_done:
339479045f13Schristos 	*datap = cpend;
339579045f13Schristos 	*datalen = 0;
339679045f13Schristos 	return FALSE;
3397abb0f93cSkardel }
3398abb0f93cSkardel 
3399abb0f93cSkardel 
34002950cc38Schristos u_short
34012950cc38Schristos varfmt(const char * varname)
3402abb0f93cSkardel {
34032950cc38Schristos 	u_int n;
3404abb0f93cSkardel 
34052950cc38Schristos 	for (n = 0; n < COUNTOF(cookedvars); n++)
34062950cc38Schristos 		if (!strcmp(varname, cookedvars[n].varname))
34072950cc38Schristos 			return cookedvars[n].fmt;
3408abb0f93cSkardel 
34092950cc38Schristos 	return PADDING;
34102950cc38Schristos }
3411abb0f93cSkardel 
3412abb0f93cSkardel 
3413abb0f93cSkardel /*
3414abb0f93cSkardel  * printvars - print variables returned in response packet
3415abb0f93cSkardel  */
3416abb0f93cSkardel void
3417abb0f93cSkardel printvars(
34188b8da087Schristos 	size_t length,
34193123f114Skardel 	const char *data,
3420abb0f93cSkardel 	int status,
3421abb0f93cSkardel 	int sttype,
3422abb0f93cSkardel 	int quiet,
3423abb0f93cSkardel 	FILE *fp
3424abb0f93cSkardel 	)
3425abb0f93cSkardel {
3426abb0f93cSkardel 	if (rawmode)
3427abb0f93cSkardel 	    rawprint(sttype, length, data, status, quiet, fp);
3428abb0f93cSkardel 	else
3429abb0f93cSkardel 	    cookedprint(sttype, length, data, status, quiet, fp);
3430abb0f93cSkardel }
3431abb0f93cSkardel 
3432abb0f93cSkardel 
3433abb0f93cSkardel /*
3434abb0f93cSkardel  * rawprint - do a printout of the data in raw mode
3435abb0f93cSkardel  */
3436abb0f93cSkardel static void
3437abb0f93cSkardel rawprint(
3438abb0f93cSkardel 	int datatype,
34398b8da087Schristos 	size_t length,
34403123f114Skardel 	const char *data,
3441abb0f93cSkardel 	int status,
3442abb0f93cSkardel 	int quiet,
3443abb0f93cSkardel 	FILE *fp
3444abb0f93cSkardel 	)
3445abb0f93cSkardel {
34463123f114Skardel 	const char *cp;
34473123f114Skardel 	const char *cpend;
3448abb0f93cSkardel 
3449abb0f93cSkardel 	/*
3450abb0f93cSkardel 	 * Essentially print the data as is.  We reformat unprintables, though.
3451abb0f93cSkardel 	 */
3452abb0f93cSkardel 	cp = data;
3453abb0f93cSkardel 	cpend = data + length;
3454abb0f93cSkardel 
3455abb0f93cSkardel 	if (!quiet)
3456abb0f93cSkardel 		(void) fprintf(fp, "status=0x%04x,\n", status);
3457abb0f93cSkardel 
3458abb0f93cSkardel 	while (cp < cpend) {
3459abb0f93cSkardel 		if (*cp == '\r') {
3460abb0f93cSkardel 			/*
3461abb0f93cSkardel 			 * If this is a \r and the next character is a
3462abb0f93cSkardel 			 * \n, supress this, else pretty print it.  Otherwise
3463abb0f93cSkardel 			 * just output the character.
3464abb0f93cSkardel 			 */
3465abb0f93cSkardel 			if (cp == (cpend - 1) || *(cp + 1) != '\n')
3466abb0f93cSkardel 			    makeascii(1, cp, fp);
34674eea345dSchristos 		} else if (isspace(pgetc(cp)) || isprint(pgetc(cp)))
3468abb0f93cSkardel 			putc(*cp, fp);
3469abb0f93cSkardel 		else
3470abb0f93cSkardel 			makeascii(1, cp, fp);
3471abb0f93cSkardel 		cp++;
3472abb0f93cSkardel 	}
3473abb0f93cSkardel }
3474abb0f93cSkardel 
3475abb0f93cSkardel 
3476abb0f93cSkardel /*
3477abb0f93cSkardel  * Global data used by the cooked output routines
3478abb0f93cSkardel  */
3479abb0f93cSkardel int out_chars;		/* number of characters output */
3480abb0f93cSkardel int out_linecount;	/* number of characters output on this line */
3481abb0f93cSkardel 
3482abb0f93cSkardel 
3483abb0f93cSkardel /*
3484abb0f93cSkardel  * startoutput - get ready to do cooked output
3485abb0f93cSkardel  */
3486abb0f93cSkardel static void
3487abb0f93cSkardel startoutput(void)
3488abb0f93cSkardel {
3489abb0f93cSkardel 	out_chars = 0;
3490abb0f93cSkardel 	out_linecount = 0;
3491abb0f93cSkardel }
3492abb0f93cSkardel 
3493abb0f93cSkardel 
3494abb0f93cSkardel /*
3495abb0f93cSkardel  * output - output a variable=value combination
3496abb0f93cSkardel  */
3497abb0f93cSkardel static void
3498abb0f93cSkardel output(
3499abb0f93cSkardel 	FILE *fp,
35002950cc38Schristos 	const char *name,
35013123f114Skardel 	const char *value
3502abb0f93cSkardel 	)
3503abb0f93cSkardel {
35048b8da087Schristos 	int len;
3505abb0f93cSkardel 
3506abb0f93cSkardel 	/* strlen of "name=value" */
35078b8da087Schristos 	len = size2int_sat(strlen(name) + 1 + strlen(value));
3508abb0f93cSkardel 
3509abb0f93cSkardel 	if (out_chars != 0) {
3510abb0f93cSkardel 		out_chars += 2;
3511abb0f93cSkardel 		if ((out_linecount + len + 2) > MAXOUTLINE) {
3512abb0f93cSkardel 			fputs(",\n", fp);
3513abb0f93cSkardel 			out_linecount = 0;
3514abb0f93cSkardel 		} else {
3515abb0f93cSkardel 			fputs(", ", fp);
3516abb0f93cSkardel 			out_linecount += 2;
3517abb0f93cSkardel 		}
3518abb0f93cSkardel 	}
3519abb0f93cSkardel 
3520abb0f93cSkardel 	fputs(name, fp);
3521abb0f93cSkardel 	putc('=', fp);
3522abb0f93cSkardel 	fputs(value, fp);
3523abb0f93cSkardel 	out_chars += len;
3524abb0f93cSkardel 	out_linecount += len;
3525abb0f93cSkardel }
3526abb0f93cSkardel 
3527abb0f93cSkardel 
3528abb0f93cSkardel /*
3529abb0f93cSkardel  * endoutput - terminate a block of cooked output
3530abb0f93cSkardel  */
3531abb0f93cSkardel static void
3532abb0f93cSkardel endoutput(
3533abb0f93cSkardel 	FILE *fp
3534abb0f93cSkardel 	)
3535abb0f93cSkardel {
3536abb0f93cSkardel 	if (out_chars != 0)
3537abb0f93cSkardel 		putc('\n', fp);
3538abb0f93cSkardel }
3539abb0f93cSkardel 
3540abb0f93cSkardel 
3541abb0f93cSkardel /*
3542abb0f93cSkardel  * outputarr - output an array of values
3543abb0f93cSkardel  */
3544abb0f93cSkardel static void
3545abb0f93cSkardel outputarr(
3546abb0f93cSkardel 	FILE *fp,
3547abb0f93cSkardel 	char *name,
3548abb0f93cSkardel 	int narr,
3549cdfa2a7eSchristos 	l_fp *lfp,
3550cdfa2a7eSchristos 	int issigned
3551abb0f93cSkardel 	)
3552abb0f93cSkardel {
35538b8da087Schristos 	char *bp;
35548b8da087Schristos 	char *cp;
35558b8da087Schristos 	size_t i;
35568b8da087Schristos 	size_t len;
3557abb0f93cSkardel 	char buf[256];
3558abb0f93cSkardel 
3559abb0f93cSkardel 	bp = buf;
3560abb0f93cSkardel 	/*
3561abb0f93cSkardel 	 * Hack to align delay and offset values
3562abb0f93cSkardel 	 */
3563abb0f93cSkardel 	for (i = (int)strlen(name); i < 11; i++)
3564abb0f93cSkardel 		*bp++ = ' ';
3565abb0f93cSkardel 
3566abb0f93cSkardel 	for (i = narr; i > 0; i--) {
35678b8da087Schristos 		if (i != (size_t)narr)
3568abb0f93cSkardel 			*bp++ = ' ';
3569cdfa2a7eSchristos 		cp = (issigned ? lfptoms(lfp, 2) : ulfptoms(lfp, 2));
3570abb0f93cSkardel 		len = strlen(cp);
3571abb0f93cSkardel 		if (len > 7) {
3572abb0f93cSkardel 			cp[7] = '\0';
3573abb0f93cSkardel 			len = 7;
3574abb0f93cSkardel 		}
3575abb0f93cSkardel 		while (len < 7) {
3576abb0f93cSkardel 			*bp++ = ' ';
3577abb0f93cSkardel 			len++;
3578abb0f93cSkardel 		}
3579abb0f93cSkardel 		while (*cp != '\0')
3580abb0f93cSkardel 		    *bp++ = *cp++;
3581abb0f93cSkardel 		lfp++;
3582abb0f93cSkardel 	}
3583abb0f93cSkardel 	*bp = '\0';
3584abb0f93cSkardel 	output(fp, name, buf);
3585abb0f93cSkardel }
3586abb0f93cSkardel 
3587abb0f93cSkardel static char *
3588abb0f93cSkardel tstflags(
3589abb0f93cSkardel 	u_long val
3590abb0f93cSkardel 	)
3591abb0f93cSkardel {
359279045f13Schristos #	if CBLEN < 10
3593*eabc0478Schristos #	 error CBLEN is too small -- increase!
359479045f13Schristos #	endif
3595abb0f93cSkardel 
359679045f13Schristos 	char *cp, *s;
359779045f13Schristos 	size_t cb, i;
359879045f13Schristos 	int l;
359979045f13Schristos 
36003123f114Skardel 	s = cp = circ_buf[nextcb];
3601abb0f93cSkardel 	if (++nextcb >= NUMCB)
3602abb0f93cSkardel 		nextcb = 0;
36033123f114Skardel 	cb = sizeof(circ_buf[0]);
3604abb0f93cSkardel 
360579045f13Schristos 	l = snprintf(cp, cb, "%02lx", val);
360679045f13Schristos 	if (l < 0 || (size_t)l >= cb)
360779045f13Schristos 		goto fail;
360879045f13Schristos 	cp += l;
360979045f13Schristos 	cb -= l;
3610abb0f93cSkardel 	if (!val) {
361179045f13Schristos 		l = strlcat(cp, " ok", cb);
361279045f13Schristos 		if ((size_t)l >= cb)
361379045f13Schristos 			goto fail;
361479045f13Schristos 		cp += l;
361579045f13Schristos 		cb -= l;
3616abb0f93cSkardel 	} else {
361779045f13Schristos 		const char *sep;
361879045f13Schristos 
361979045f13Schristos 		sep = " ";
362079045f13Schristos 		for (i = 0; i < COUNTOF(tstflagnames); i++) {
3621abb0f93cSkardel 			if (val & 0x1) {
362279045f13Schristos 				l = snprintf(cp, cb, "%s%s", sep,
36233123f114Skardel 					     tstflagnames[i]);
362479045f13Schristos 				if (l < 0)
362579045f13Schristos 					goto fail;
362679045f13Schristos 				if ((size_t)l >= cb) {
362779045f13Schristos 					cp += cb - 4;
362879045f13Schristos 					cb = 4;
362979045f13Schristos 					l = strlcpy (cp, "...", cb);
363079045f13Schristos 					cp += l;
363179045f13Schristos 					cb -= l;
363279045f13Schristos 					break;
363379045f13Schristos 				}
3634abb0f93cSkardel 				sep = ", ";
363579045f13Schristos 				cp += l;
363679045f13Schristos 				cb -= l;
3637abb0f93cSkardel 			}
3638abb0f93cSkardel 			val >>= 1;
3639abb0f93cSkardel 		}
3640abb0f93cSkardel 	}
36413123f114Skardel 
3642abb0f93cSkardel 	return s;
364379045f13Schristos 
364479045f13Schristos   fail:
364579045f13Schristos 	*cp = '\0';
364679045f13Schristos 	return s;
3647abb0f93cSkardel }
3648abb0f93cSkardel 
3649abb0f93cSkardel /*
3650abb0f93cSkardel  * cookedprint - output variables in cooked mode
3651abb0f93cSkardel  */
3652abb0f93cSkardel static void
3653abb0f93cSkardel cookedprint(
3654abb0f93cSkardel 	int datatype,
36558b8da087Schristos 	size_t length,
36563123f114Skardel 	const char *data,
3657abb0f93cSkardel 	int status,
3658abb0f93cSkardel 	int quiet,
3659abb0f93cSkardel 	FILE *fp
3660abb0f93cSkardel 	)
3661abb0f93cSkardel {
3662abb0f93cSkardel 	char *name;
3663abb0f93cSkardel 	char *value;
3664abb0f93cSkardel 	char output_raw;
3665abb0f93cSkardel 	int fmt;
3666abb0f93cSkardel 	l_fp lfp;
3667abb0f93cSkardel 	sockaddr_u hval;
3668abb0f93cSkardel 	u_long uval;
3669abb0f93cSkardel 	int narr;
36702950cc38Schristos 	size_t len;
36712950cc38Schristos 	l_fp lfparr[8];
36722950cc38Schristos 	char b[12];
36732950cc38Schristos 	char bn[2 * MAXVARLEN];
36742950cc38Schristos 	char bv[2 * MAXVALLEN];
3675abb0f93cSkardel 
36762950cc38Schristos 	UNUSED_ARG(datatype);
3677abb0f93cSkardel 
3678abb0f93cSkardel 	if (!quiet)
3679abb0f93cSkardel 		fprintf(fp, "status=%04x %s,\n", status,
3680abb0f93cSkardel 			statustoa(datatype, status));
3681abb0f93cSkardel 
3682abb0f93cSkardel 	startoutput();
3683abb0f93cSkardel 	while (nextvar(&length, &data, &name, &value)) {
36842950cc38Schristos 		fmt = varfmt(name);
3685abb0f93cSkardel 		output_raw = 0;
3686abb0f93cSkardel 		switch (fmt) {
36872950cc38Schristos 
36882950cc38Schristos 		case PADDING:
36892950cc38Schristos 			output_raw = '*';
36902950cc38Schristos 			break;
36912950cc38Schristos 
3692abb0f93cSkardel 		case TS:
36934eea345dSchristos 			if (!value || !decodets(value, &lfp))
3694abb0f93cSkardel 				output_raw = '?';
3695abb0f93cSkardel 			else
3696abb0f93cSkardel 				output(fp, name, prettydate(&lfp));
3697abb0f93cSkardel 			break;
3698abb0f93cSkardel 
36992950cc38Schristos 		case HA:	/* fallthru */
3700abb0f93cSkardel 		case NA:
37014eea345dSchristos 			if (!value || !decodenetnum(value, &hval)) {
3702abb0f93cSkardel 				output_raw = '?';
37032950cc38Schristos 			} else if (fmt == HA){
3704abb0f93cSkardel 				output(fp, name, nntohost(&hval));
3705abb0f93cSkardel 			} else {
3706abb0f93cSkardel 				output(fp, name, stoa(&hval));
3707abb0f93cSkardel 			}
3708abb0f93cSkardel 			break;
3709abb0f93cSkardel 
3710abb0f93cSkardel 		case RF:
37114eea345dSchristos 			if (!value) {
37124eea345dSchristos 				output_raw = '?';
37134eea345dSchristos 			} else if (decodenetnum(value, &hval)) {
3714cdfa2a7eSchristos 				if (datatype == TYPE_CLOCK && IS_IPV4(&hval)) {
3715cdfa2a7eSchristos 					/*
3716cdfa2a7eSchristos 					 * Workaround to override numeric refid formats
3717cdfa2a7eSchristos 					 * for refclocks received from faulty nptd servers
3718cdfa2a7eSchristos 					 * and output them as text.
3719cdfa2a7eSchristos 					 */
3720cdfa2a7eSchristos 					int i;
3721cdfa2a7eSchristos 					unsigned char *str = (unsigned char *)&(hval.sa4).sin_addr;
3722cdfa2a7eSchristos 					char refid_buf[5];
3723cdfa2a7eSchristos 					for (i=0; i<4 && str[i]; i++)
3724cdfa2a7eSchristos 						refid_buf[i] = (isprint(str[i]) ? str[i] : '?');
3725cdfa2a7eSchristos 					refid_buf[i] = 0; /* Null terminator */
3726cdfa2a7eSchristos 					output(fp, name, refid_buf);
3727cdfa2a7eSchristos 				} else if (ISREFCLOCKADR(&hval)) {
3728cdfa2a7eSchristos 					output(fp, name, refnumtoa(&hval));
3729cdfa2a7eSchristos 				} else {
3730cdfa2a7eSchristos 					if (drefid == REFID_IPV4) {
3731abb0f93cSkardel 						output(fp, name, stoa(&hval));
3732cdfa2a7eSchristos 					} else {
3733cdfa2a7eSchristos 						char refid_buf[12];
3734cdfa2a7eSchristos 						snprintf (refid_buf, sizeof(refid_buf),
3735cdfa2a7eSchristos 							  "0x%08x", ntohl(addr2refid(&hval)));
3736cdfa2a7eSchristos 						output(fp, name, refid_buf);
3737cdfa2a7eSchristos 					}
3738cdfa2a7eSchristos 				}
37392950cc38Schristos 			} else if (strlen(value) <= 4) {
3740abb0f93cSkardel 				output(fp, name, value);
37412950cc38Schristos 			} else {
3742abb0f93cSkardel 				output_raw = '?';
37432950cc38Schristos 			}
3744abb0f93cSkardel 			break;
3745abb0f93cSkardel 
3746abb0f93cSkardel 		case LP:
37474eea345dSchristos 			if (!value || !decodeuint(value, &uval) || uval > 3) {
3748abb0f93cSkardel 				output_raw = '?';
37492950cc38Schristos 			} else {
37502950cc38Schristos 				b[0] = (0x2 & uval)
37512950cc38Schristos 					   ? '1'
37522950cc38Schristos 					   : '0';
37532950cc38Schristos 				b[1] = (0x1 & uval)
37542950cc38Schristos 					   ? '1'
37552950cc38Schristos 					   : '0';
3756abb0f93cSkardel 				b[2] = '\0';
3757abb0f93cSkardel 				output(fp, name, b);
3758abb0f93cSkardel 			}
3759abb0f93cSkardel 			break;
3760abb0f93cSkardel 
3761abb0f93cSkardel 		case OC:
37624eea345dSchristos 			if (!value || !decodeuint(value, &uval)) {
3763abb0f93cSkardel 				output_raw = '?';
37642950cc38Schristos 			} else {
37652950cc38Schristos 				snprintf(b, sizeof(b), "%03lo", uval);
3766abb0f93cSkardel 				output(fp, name, b);
3767abb0f93cSkardel 			}
3768abb0f93cSkardel 			break;
3769abb0f93cSkardel 
3770cdfa2a7eSchristos 		case AU:
3771cdfa2a7eSchristos 		case AS:
37724eea345dSchristos 			if (!value || !decodearr(value, &narr, lfparr, 8))
3773abb0f93cSkardel 				output_raw = '?';
3774abb0f93cSkardel 			else
3775cdfa2a7eSchristos 				outputarr(fp, name, narr, lfparr, (fmt==AS));
3776abb0f93cSkardel 			break;
3777abb0f93cSkardel 
3778abb0f93cSkardel 		case FX:
37794eea345dSchristos 			if (!value || !decodeuint(value, &uval))
3780abb0f93cSkardel 				output_raw = '?';
3781abb0f93cSkardel 			else
3782abb0f93cSkardel 				output(fp, name, tstflags(uval));
3783abb0f93cSkardel 			break;
3784abb0f93cSkardel 
3785cdfa2a7eSchristos 		case SN:
3786cdfa2a7eSchristos 			if (!value)
3787cdfa2a7eSchristos 				output_raw = '?';
37887ee11f2fSchristos 			else if (isdigit(*(const unsigned char *)value)) {	/* number without sign */
3789cdfa2a7eSchristos 				bv[0] = '+';
3790cdfa2a7eSchristos 				atoascii (value, MAXVALLEN, bv+1, sizeof(bv)-1);
3791cdfa2a7eSchristos 				output(fp, name, bv);
3792cdfa2a7eSchristos 			} else
3793cdfa2a7eSchristos 				output_raw = '*';		/* output as-is */
3794cdfa2a7eSchristos 			break;
3795cdfa2a7eSchristos 
3796abb0f93cSkardel 		default:
37972950cc38Schristos 			fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3798abb0f93cSkardel 				name, value, fmt);
37992950cc38Schristos 			output_raw = '?';
3800abb0f93cSkardel 			break;
3801abb0f93cSkardel 		}
3802abb0f93cSkardel 
3803abb0f93cSkardel 		if (output_raw != 0) {
3804af12ab5eSchristos 			/* TALOS-CAN-0063: avoid buffer overrun */
3805abb0f93cSkardel 			atoascii(name, MAXVARLEN, bn, sizeof(bn));
3806abb0f93cSkardel 			if (output_raw != '*') {
3807af12ab5eSchristos 				atoascii(value, MAXVALLEN,
3808af12ab5eSchristos 					 bv, sizeof(bv) - 1);
3809abb0f93cSkardel 				len = strlen(bv);
3810abb0f93cSkardel 				bv[len] = output_raw;
3811abb0f93cSkardel 				bv[len+1] = '\0';
3812af12ab5eSchristos 			} else {
3813af12ab5eSchristos 				atoascii(value, MAXVALLEN,
3814af12ab5eSchristos 					 bv, sizeof(bv));
3815abb0f93cSkardel 			}
3816abb0f93cSkardel 			output(fp, bn, bv);
3817abb0f93cSkardel 		}
3818abb0f93cSkardel 	}
3819abb0f93cSkardel 	endoutput(fp);
3820abb0f93cSkardel }
3821abb0f93cSkardel 
3822abb0f93cSkardel 
3823abb0f93cSkardel /*
3824abb0f93cSkardel  * sortassoc - sort associations in the cache into ascending order
3825abb0f93cSkardel  */
3826abb0f93cSkardel void
3827abb0f93cSkardel sortassoc(void)
3828abb0f93cSkardel {
3829abb0f93cSkardel 	if (numassoc > 1)
38302950cc38Schristos 		qsort(assoc_cache, (size_t)numassoc,
38312950cc38Schristos 		      sizeof(assoc_cache[0]), &assoccmp);
3832abb0f93cSkardel }
3833abb0f93cSkardel 
3834abb0f93cSkardel 
3835abb0f93cSkardel /*
3836abb0f93cSkardel  * assoccmp - compare two associations
3837abb0f93cSkardel  */
3838abb0f93cSkardel static int
3839abb0f93cSkardel assoccmp(
3840abb0f93cSkardel 	const void *t1,
3841abb0f93cSkardel 	const void *t2
3842abb0f93cSkardel 	)
3843abb0f93cSkardel {
38443123f114Skardel 	const struct association *ass1 = t1;
38453123f114Skardel 	const struct association *ass2 = t2;
3846abb0f93cSkardel 
3847abb0f93cSkardel 	if (ass1->assid < ass2->assid)
3848abb0f93cSkardel 		return -1;
3849abb0f93cSkardel 	if (ass1->assid > ass2->assid)
3850abb0f93cSkardel 		return 1;
3851abb0f93cSkardel 	return 0;
3852abb0f93cSkardel }
38533123f114Skardel 
3854abb0f93cSkardel 
3855abb0f93cSkardel /*
38562950cc38Schristos  * grow_assoc_cache() - enlarge dynamic assoc_cache array
38572950cc38Schristos  *
38582950cc38Schristos  * The strategy is to add an assumed 4k page size at a time, leaving
38592950cc38Schristos  * room for malloc() bookkeeping overhead equivalent to 4 pointers.
38602950cc38Schristos  */
38612950cc38Schristos void
38622950cc38Schristos grow_assoc_cache(void)
38632950cc38Schristos {
38642950cc38Schristos 	static size_t	prior_sz;
38652950cc38Schristos 	size_t		new_sz;
38662950cc38Schristos 
38672950cc38Schristos 	new_sz = prior_sz + 4 * 1024;
38682950cc38Schristos 	if (0 == prior_sz) {
38692950cc38Schristos 		new_sz -= 4 * sizeof(void *);
38702950cc38Schristos 	}
38712950cc38Schristos 	assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
38722950cc38Schristos 	prior_sz = new_sz;
38738b8da087Schristos 	assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
38742950cc38Schristos }
38752950cc38Schristos 
38762950cc38Schristos 
38772950cc38Schristos /*
3878abb0f93cSkardel  * ntpq_custom_opt_handler - autoopts handler for -c and -p
3879abb0f93cSkardel  *
3880abb0f93cSkardel  * By default, autoopts loses the relative order of -c and -p options
3881abb0f93cSkardel  * on the command line.  This routine replaces the default handler for
3882abb0f93cSkardel  * those routines and builds a list of commands to execute preserving
3883abb0f93cSkardel  * the order.
3884abb0f93cSkardel  */
3885abb0f93cSkardel void
3886abb0f93cSkardel ntpq_custom_opt_handler(
3887abb0f93cSkardel 	tOptions *pOptions,
3888abb0f93cSkardel 	tOptDesc *pOptDesc
3889abb0f93cSkardel 	)
3890abb0f93cSkardel {
3891abb0f93cSkardel 	switch (pOptDesc->optValue) {
3892abb0f93cSkardel 
3893abb0f93cSkardel 	default:
3894abb0f93cSkardel 		fprintf(stderr,
3895abb0f93cSkardel 			"ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3896abb0f93cSkardel 			pOptDesc->optValue, pOptDesc->optValue);
38972950cc38Schristos 		exit(1);
3898abb0f93cSkardel 
3899abb0f93cSkardel 	case 'c':
3900*eabc0478Schristos 		if ((pOptDesc->fOptState & OPTST_SET_MASK) == OPTST_DEFINED)
3901*eabc0478Schristos 			defcmds++;
3902abb0f93cSkardel 		ADDCMD(pOptDesc->pzLastArg);
3903abb0f93cSkardel 		break;
3904abb0f93cSkardel 
3905abb0f93cSkardel 	case 'p':
3906*eabc0478Schristos 		if ((pOptDesc->fOptState & OPTST_SET_MASK) == OPTST_DEFINED)
3907*eabc0478Schristos 			defcmds++;
3908abb0f93cSkardel 		ADDCMD("peers");
3909abb0f93cSkardel 		break;
3910abb0f93cSkardel 	}
3911abb0f93cSkardel }
39125d681e99Schristos /*
39135d681e99Schristos  * Obtain list of digest names
39145d681e99Schristos  */
39155d681e99Schristos 
39164eea345dSchristos #if defined(OPENSSL) && !defined(HAVE_EVP_MD_DO_ALL_SORTED)
39174eea345dSchristos # if defined(_MSC_VER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
39184eea345dSchristos #  define HAVE_EVP_MD_DO_ALL_SORTED
39194eea345dSchristos # endif
39204eea345dSchristos #endif
39214eea345dSchristos 
39225d681e99Schristos #ifdef OPENSSL
39235d681e99Schristos # ifdef HAVE_EVP_MD_DO_ALL_SORTED
39244eea345dSchristos #  define K_PER_LINE	8
39254eea345dSchristos #  define K_NL_PFX_STR	"\n    "
39264eea345dSchristos #  define K_DELIM_STR	", "
39274eea345dSchristos 
39285d681e99Schristos struct hstate {
39295d681e99Schristos 	char *list;
3930*eabc0478Schristos 	char const **seen;
39315d681e99Schristos 	int idx;
39325d681e99Schristos };
39334eea345dSchristos 
39344eea345dSchristos 
393579045f13Schristos #  ifndef BUILD_AS_LIB
39364eea345dSchristos static void
39374eea345dSchristos list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg)
39385d681e99Schristos {
39395d681e99Schristos 	size_t 	       len, n;
39404eea345dSchristos 	const char    *name, **seen;
39415d681e99Schristos 	struct hstate *hstate = arg;
39425d681e99Schristos 
39434eea345dSchristos 	/* m is MD obj, from is name or alias, to is base name for alias */
3944*eabc0478Schristos 	if (!m || !from || to) {
39455d681e99Schristos 		return; /* Ignore aliases */
3946*eabc0478Schristos 	}
39474eea345dSchristos 
39484eea345dSchristos 	/* Discard MACs that NTP won't accept. */
39494eea345dSchristos 	/* Keep this consistent with keytype_from_text() in ssl_init.c. */
3950*eabc0478Schristos 	if ((size_t)EVP_MD_size(m) > MAX_MDG_LEN) {
39514eea345dSchristos 		return;
3952*eabc0478Schristos 	}
39535d681e99Schristos 
39545d681e99Schristos 	name = EVP_MD_name(m);
3955*eabc0478Schristos 	len = strlen(name) + 1;
39565d681e99Schristos 
39575d681e99Schristos 	/* There are duplicates.  Discard if name has been seen. */
39585d681e99Schristos 
395979045f13Schristos 	for (seen = hstate->seen; *seen; seen++)
3960*eabc0478Schristos 		if (!strcasecmp(*seen, name))
39615d681e99Schristos 			return;
39624eea345dSchristos 
39635d681e99Schristos 	n = (seen - hstate->seen) + 2;
3964*eabc0478Schristos 	hstate->seen = erealloc((void *)hstate->seen, n * sizeof(*seen));
39655d681e99Schristos 	hstate->seen[n-2] = name;
39665d681e99Schristos 	hstate->seen[n-1] = NULL;
39675d681e99Schristos 
396879045f13Schristos 	if (hstate->list != NULL)
39695d681e99Schristos 		len += strlen(hstate->list);
39704eea345dSchristos 
39714eea345dSchristos 	len += (hstate->idx >= K_PER_LINE)
39724eea345dSchristos 	    ? strlen(K_NL_PFX_STR)
39734eea345dSchristos 	    : strlen(K_DELIM_STR);
39745d681e99Schristos 
39755d681e99Schristos 	if (hstate->list == NULL) {
3976af12ab5eSchristos 		hstate->list = (char *)emalloc(len);
39775d681e99Schristos 		hstate->list[0] = '\0';
39784eea345dSchristos 	} else {
3979af12ab5eSchristos 		hstate->list = (char *)erealloc(hstate->list, len);
39804eea345dSchristos 	}
39815d681e99Schristos 
39825d681e99Schristos 	sprintf(hstate->list + strlen(hstate->list), "%s%s",
39835d681e99Schristos 		((hstate->idx >= K_PER_LINE) ? K_NL_PFX_STR : K_DELIM_STR),
39845d681e99Schristos 		name);
39854eea345dSchristos 
398679045f13Schristos 	if (hstate->idx >= K_PER_LINE)
39875d681e99Schristos 		hstate->idx = 1;
398879045f13Schristos 	else
39895d681e99Schristos 		hstate->idx++;
39905d681e99Schristos }
399179045f13Schristos #  endif /* !defined(BUILD_AS_LIB) */
39924eea345dSchristos 
399379045f13Schristos #  ifndef BUILD_AS_LIB
39944eea345dSchristos /* Insert CMAC into SSL digests list */
39954eea345dSchristos static char *
39964eea345dSchristos insert_cmac(char *list)
39974eea345dSchristos {
399879045f13Schristos #ifdef ENABLE_CMAC
39994eea345dSchristos 	int insert;
40004eea345dSchristos 	size_t len;
40014eea345dSchristos 
40024eea345dSchristos 
40034eea345dSchristos 	/* If list empty, we need to insert CMAC on new line */
40044eea345dSchristos 	insert = (!list || !*list);
40054eea345dSchristos 
40064eea345dSchristos 	if (insert) {
40074eea345dSchristos 		len = strlen(K_NL_PFX_STR) + strlen(CMAC);
40084eea345dSchristos 		list = (char *)erealloc(list, len + 1);
40094eea345dSchristos 		sprintf(list, "%s%s", K_NL_PFX_STR, CMAC);
40104eea345dSchristos 	} else {	/* List not empty */
40114eea345dSchristos 		/* Check if CMAC already in list - future proofing */
40124eea345dSchristos 		const char *cmac_sn;
40134eea345dSchristos 		char *cmac_p;
40144eea345dSchristos 
40154eea345dSchristos 		cmac_sn = OBJ_nid2sn(NID_cmac);
40164eea345dSchristos 		cmac_p = list;
40174eea345dSchristos 		insert = cmac_sn != NULL && *cmac_sn != '\0';
40184eea345dSchristos 
40194eea345dSchristos 		/* CMAC in list if found, followed by nul char or ',' */
40204eea345dSchristos 		while (insert && NULL != (cmac_p = strstr(cmac_p, cmac_sn))) {
40214eea345dSchristos 			cmac_p += strlen(cmac_sn);
40224eea345dSchristos 			/* Still need to insert if not nul and not ',' */
40234eea345dSchristos 			insert = *cmac_p && ',' != *cmac_p;
40244eea345dSchristos 		}
40254eea345dSchristos 
40264eea345dSchristos 		/* Find proper insertion point */
40274eea345dSchristos 		if (insert) {
40284eea345dSchristos 			char *last_nl;
40294eea345dSchristos 			char *point;
40304eea345dSchristos 			char *delim;
40314eea345dSchristos 			int found;
40324eea345dSchristos 
40334eea345dSchristos 			/* Default to start if list empty */
40344eea345dSchristos 			found = 0;
40354eea345dSchristos 			delim = list;
40364eea345dSchristos 			len = strlen(list);
40374eea345dSchristos 
40384eea345dSchristos 			/* While new lines */
40394eea345dSchristos 			while (delim < list + len && *delim &&
40404eea345dSchristos 			       !strncmp(K_NL_PFX_STR, delim, strlen(K_NL_PFX_STR))) {
40414eea345dSchristos 				point = delim + strlen(K_NL_PFX_STR);
40424eea345dSchristos 
40434eea345dSchristos 				/* While digest names on line */
40444eea345dSchristos 				while (point < list + len && *point) {
40454eea345dSchristos 					/* Another digest after on same or next line? */
40464eea345dSchristos 					delim = strstr( point, K_DELIM_STR);
40474eea345dSchristos 					last_nl = strstr( point, K_NL_PFX_STR);
40484eea345dSchristos 
40494eea345dSchristos 					/* No - end of list */
40504eea345dSchristos 					if (!delim && !last_nl) {
40514eea345dSchristos 						delim = list + len;
4052*eabc0478Schristos 					} else {
40534eea345dSchristos 						/* New line and no delim or before delim? */
40544eea345dSchristos 						if (last_nl && (!delim || last_nl < delim)) {
40554eea345dSchristos 							delim = last_nl;
40564eea345dSchristos 						}
4057*eabc0478Schristos 					}
40584eea345dSchristos 
40594eea345dSchristos 					/* Found insertion point where CMAC before entry? */
40604eea345dSchristos 					if (strncmp(CMAC, point, delim - point) < 0) {
40614eea345dSchristos 						found = 1;
40624eea345dSchristos 						break;
40634eea345dSchristos 					}
40644eea345dSchristos 
40654eea345dSchristos 					if (delim < list + len && *delim &&
40664eea345dSchristos 					    !strncmp(K_DELIM_STR, delim, strlen(K_DELIM_STR))) {
40674eea345dSchristos 						point += strlen(K_DELIM_STR);
40684eea345dSchristos 					} else {
40694eea345dSchristos 						break;
40704eea345dSchristos 					}
40714eea345dSchristos 				} /* While digest names on line */
40724eea345dSchristos 			} /* While new lines */
40734eea345dSchristos 
40744eea345dSchristos 			/* If found in list */
40754eea345dSchristos 			if (found) {
40764eea345dSchristos 				/* insert cmac and delim */
40774eea345dSchristos 				/* Space for list could move - save offset */
40784eea345dSchristos 				ptrdiff_t p_offset = point - list;
40794eea345dSchristos 				len += strlen(CMAC) + strlen(K_DELIM_STR);
40804eea345dSchristos 				list = (char *)erealloc(list, len + 1);
40814eea345dSchristos 				point = list + p_offset;
40824eea345dSchristos 				/* move to handle src/dest overlap */
40834eea345dSchristos 				memmove(point + strlen(CMAC) + strlen(K_DELIM_STR),
40844eea345dSchristos 					point, strlen(point) + 1);
4085*eabc0478Schristos 				memcpy(point, CMAC, strlen(CMAC));
4086*eabc0478Schristos 				memcpy(point + strlen(CMAC), K_DELIM_STR, strlen(K_DELIM_STR));
40874eea345dSchristos 			} else {	/* End of list */
40884eea345dSchristos 				/* append delim and cmac */
40894eea345dSchristos 				len += strlen(K_DELIM_STR) + strlen(CMAC);
40904eea345dSchristos 				list = (char *)erealloc(list, len + 1);
40914eea345dSchristos 				strcpy(list + strlen(list), K_DELIM_STR);
40924eea345dSchristos 				strcpy(list + strlen(list), CMAC);
40934eea345dSchristos 			}
40944eea345dSchristos 		} /* insert */
40954eea345dSchristos 	} /* List not empty */
409679045f13Schristos #endif /*ENABLE_CMAC*/
40974eea345dSchristos 	return list;
40984eea345dSchristos }
409979045f13Schristos #  endif /* !defined(BUILD_AS_LIB) */
41005d681e99Schristos # endif
41015d681e99Schristos #endif
41025d681e99Schristos 
41034eea345dSchristos 
410479045f13Schristos #ifndef BUILD_AS_LIB
41054eea345dSchristos static char *
41064eea345dSchristos list_digest_names(void)
41075d681e99Schristos {
41085d681e99Schristos 	char *list = NULL;
41095d681e99Schristos 
41105d681e99Schristos #ifdef OPENSSL
41115d681e99Schristos # ifdef HAVE_EVP_MD_DO_ALL_SORTED
41125d681e99Schristos 	struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
41135d681e99Schristos 
41144eea345dSchristos 	/* replace calloc(1, sizeof(const char *)) */
4115*eabc0478Schristos 	hstate.seen = emalloc_zero(sizeof(const char*));
41165d681e99Schristos 
41175d681e99Schristos 	INIT_SSL();
41185d681e99Schristos 	EVP_MD_do_all_sorted(list_md_fn, &hstate);
41195d681e99Schristos 	list = hstate.list;
4120*eabc0478Schristos 	free((void *)hstate.seen);
41214eea345dSchristos 
41224eea345dSchristos 	list = insert_cmac(list);	/* Insert CMAC into SSL digests list */
41234eea345dSchristos 
41245d681e99Schristos # else
4125af12ab5eSchristos 	list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
41265d681e99Schristos 	strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
41275d681e99Schristos # endif
41285d681e99Schristos #else
4129af12ab5eSchristos 	list = (char *)emalloc(sizeof("md5"));
41305d681e99Schristos 	strcpy(list, "md5");
41315d681e99Schristos #endif
41325d681e99Schristos 
41335d681e99Schristos 	return list;
41345d681e99Schristos }
413579045f13Schristos #endif /* !defined(BUILD_AS_LIB) */
41368b8da087Schristos 
41378b8da087Schristos #define CTRLC_STACK_MAX 4
41388b8da087Schristos static volatile size_t		ctrlc_stack_len = 0;
41398b8da087Schristos static volatile Ctrl_C_Handler	ctrlc_stack[CTRLC_STACK_MAX];
41408b8da087Schristos 
41418b8da087Schristos 
41428b8da087Schristos 
41438b8da087Schristos int/*BOOL*/
41448b8da087Schristos push_ctrl_c_handler(
41458b8da087Schristos 	Ctrl_C_Handler func
41468b8da087Schristos 	)
41478b8da087Schristos {
41488b8da087Schristos 	size_t size = ctrlc_stack_len;
41498b8da087Schristos 	if (func && (size < CTRLC_STACK_MAX)) {
41508b8da087Schristos 		ctrlc_stack[size] = func;
41518b8da087Schristos 		ctrlc_stack_len = size + 1;
41528b8da087Schristos 		return TRUE;
41538b8da087Schristos 	}
41548b8da087Schristos 	return FALSE;
41558b8da087Schristos }
41568b8da087Schristos 
41578b8da087Schristos int/*BOOL*/
41588b8da087Schristos pop_ctrl_c_handler(
41598b8da087Schristos 	Ctrl_C_Handler func
41608b8da087Schristos 	)
41618b8da087Schristos {
41628b8da087Schristos 	size_t size = ctrlc_stack_len;
41638b8da087Schristos 	if (size) {
41648b8da087Schristos 		--size;
41658b8da087Schristos 		if (func == NULL || func == ctrlc_stack[size]) {
41668b8da087Schristos 			ctrlc_stack_len = size;
41678b8da087Schristos 			return TRUE;
41688b8da087Schristos 		}
41698b8da087Schristos 	}
41708b8da087Schristos 	return FALSE;
41718b8da087Schristos }
41728b8da087Schristos 
417379045f13Schristos #ifndef BUILD_AS_LIB
41748b8da087Schristos static void
41758b8da087Schristos on_ctrlc(void)
41768b8da087Schristos {
41778b8da087Schristos 	size_t size = ctrlc_stack_len;
41788b8da087Schristos 	while (size)
41798b8da087Schristos 		if ((*ctrlc_stack[--size])())
41808b8da087Schristos 			break;
41818b8da087Schristos }
418279045f13Schristos #endif /* !defined(BUILD_AS_LIB) */
418368dbbb44Schristos 
418479045f13Schristos #ifndef BUILD_AS_LIB
418568dbbb44Schristos static int
418668dbbb44Schristos my_easprintf(
418768dbbb44Schristos 	char ** 	ppinto,
418868dbbb44Schristos 	const char *	fmt   ,
418968dbbb44Schristos 	...
419068dbbb44Schristos 	)
419168dbbb44Schristos {
419268dbbb44Schristos 	va_list	va;
419368dbbb44Schristos 	int	prc;
419468dbbb44Schristos 	size_t	len = 128;
419568dbbb44Schristos 	char *	buf = emalloc(len);
419668dbbb44Schristos 
419768dbbb44Schristos   again:
419868dbbb44Schristos 	/* Note: we expect the memory allocation to fail long before the
419968dbbb44Schristos 	 * increment in buffer size actually overflows.
420068dbbb44Schristos 	 */
420168dbbb44Schristos 	buf = (buf) ? erealloc(buf, len) : emalloc(len);
420268dbbb44Schristos 
420368dbbb44Schristos 	va_start(va, fmt);
420468dbbb44Schristos 	prc = vsnprintf(buf, len, fmt, va);
420568dbbb44Schristos 	va_end(va);
420668dbbb44Schristos 
420768dbbb44Schristos 	if (prc < 0) {
420868dbbb44Schristos 		/* might be very old vsnprintf. Or actually MSVC... */
420968dbbb44Schristos 		len += len >> 1;
421068dbbb44Schristos 		goto again;
421168dbbb44Schristos 	}
421268dbbb44Schristos 	if ((size_t)prc >= len) {
421368dbbb44Schristos 		/* at least we have the proper size now... */
421468dbbb44Schristos 		len = (size_t)prc + 1;
421568dbbb44Schristos 		goto again;
421668dbbb44Schristos 	}
421768dbbb44Schristos 	if ((size_t)prc < (len - 32))
421868dbbb44Schristos 		buf = erealloc(buf, (size_t)prc + 1);
421968dbbb44Schristos 	*ppinto = buf;
422068dbbb44Schristos 	return prc;
422168dbbb44Schristos }
422279045f13Schristos #endif /* !defined(BUILD_AS_LIB) */
4223