154359Sroberto/*
254359Sroberto * ntpq - query an NTP server using mode 6 commands
354359Sroberto */
4285612Sdelphij#include <config.h>
5182007Sroberto#include <ctype.h>
6182007Sroberto#include <signal.h>
7182007Sroberto#include <setjmp.h>
8330141Sdelphij#include <stddef.h>
9330141Sdelphij#include <stdio.h>
10182007Sroberto#include <sys/types.h>
11182007Sroberto#include <sys/time.h>
12285612Sdelphij#ifdef HAVE_UNISTD_H
13285612Sdelphij# include <unistd.h>
14285612Sdelphij#endif
15285612Sdelphij#ifdef HAVE_FCNTL_H
16285612Sdelphij# include <fcntl.h>
17285612Sdelphij#endif
18285612Sdelphij#ifdef SYS_WINNT
19285612Sdelphij# include <mswsock.h>
20285612Sdelphij#endif
21285612Sdelphij#include <isc/net.h>
22285612Sdelphij#include <isc/result.h>
23182007Sroberto
2482498Sroberto#include "ntpq.h"
25285612Sdelphij#include "ntp_assert.h"
26285612Sdelphij#include "ntp_stdlib.h"
2782498Sroberto#include "ntp_unixtime.h"
2882498Sroberto#include "ntp_calendar.h"
2982498Sroberto#include "ntp_select.h"
30285612Sdelphij#include "ntp_assert.h"
31285612Sdelphij#include "lib_strbuf.h"
32285612Sdelphij#include "ntp_lineedit.h"
33285612Sdelphij#include "ntp_debug.h"
34285612Sdelphij#ifdef OPENSSL
35338531Sdelphij# include "openssl/evp.h"
36338531Sdelphij# include "openssl/objects.h"
37338531Sdelphij# include "openssl/err.h"
38338531Sdelphij# ifdef SYS_WINNT
39338531Sdelphij#  include "openssl/opensslv.h"
40338531Sdelphij#  if !defined(HAVE_EVP_MD_DO_ALL_SORTED) && OPENSSL_VERSION_NUMBER > 0x10000000L
41338531Sdelphij#     define HAVE_EVP_MD_DO_ALL_SORTED	1
42338531Sdelphij#  endif
43330141Sdelphij# endif
44338531Sdelphij# include "libssl_compat.h"
45338531Sdelphij# ifdef HAVE_OPENSSL_CMAC_H
46338531Sdelphij#  include <openssl/cmac.h>
47338531Sdelphij#  define CMAC "AES128CMAC"
48338531Sdelphij# endif
49330141Sdelphij#endif
50285612Sdelphij#include <ssl_applink.c>
5182498Sroberto
52285612Sdelphij#include "ntp_libopts.h"
53293650Sglebius#include "safecast.h"
54182007Sroberto
55285612Sdelphij#ifdef SYS_VXWORKS		/* vxWorks needs mode flag -casey*/
56182007Sroberto# define open(name, flags)   open(name, flags, 0777)
57182007Sroberto# define SERVER_PORT_NUM     123
5854359Sroberto#endif
5954359Sroberto
60182007Sroberto/* we use COMMAND as an autogen keyword */
61182007Sroberto#ifdef COMMAND
62182007Sroberto# undef COMMAND
63182007Sroberto#endif
64182007Sroberto
6554359Sroberto/*
6654359Sroberto * Because we potentially understand a lot of commands we will run
6754359Sroberto * interactive if connected to a terminal.
6854359Sroberto */
6954359Srobertoint interactive = 0;		/* set to 1 when we should prompt */
7054359Srobertoconst char *prompt = "ntpq> ";	/* prompt to ask him about */
7154359Sroberto
72285612Sdelphij/*
73285612Sdelphij * use old readvars behavior?  --old-rv processing in ntpq resets
74285612Sdelphij * this value based on the presence or absence of --old-rv.  It is
75285612Sdelphij * initialized to 1 here to maintain backward compatibility with
76285612Sdelphij * libntpq clients such as ntpsnmpd, which are free to reset it as
77285612Sdelphij * desired.
78285612Sdelphij */
79285612Sdelphijint	old_rv = 1;
8054359Sroberto
81298699Sdelphij/*
82298699Sdelphij * How should we display the refid?
83298699Sdelphij * REFID_HASH, REFID_IPV4
84298699Sdelphij */
85298699Sdelphijte_Refid drefid = -1;
86285612Sdelphij
8754359Sroberto/*
88182007Sroberto * for get_systime()
89182007Sroberto */
90182007Srobertos_char	sys_precision;		/* local clock precision (log2 s) */
91182007Sroberto
92182007Sroberto/*
9354359Sroberto * Keyid used for authenticated requests.  Obtained on the fly.
9454359Sroberto */
95132451Srobertou_long info_auth_keyid = 0;
9654359Sroberto
97285612Sdelphijstatic	int	info_auth_keytype = NID_md5;	/* MD5 */
98285612Sdelphijstatic	size_t	info_auth_hashlen = 16;		/* MD5 */
9954359Srobertou_long	current_time;		/* needed by authkeys; not used */
10054359Sroberto
10154359Sroberto/*
10254359Sroberto * Flag which indicates we should always send authenticated requests
10354359Sroberto */
10454359Srobertoint always_auth = 0;
10554359Sroberto
10654359Sroberto/*
10754359Sroberto * Flag which indicates raw mode output.
10854359Sroberto */
10954359Srobertoint rawmode = 0;
11054359Sroberto
11154359Sroberto/*
11254359Sroberto * Packet version number we use
11354359Sroberto */
11454359Srobertou_char pktversion = NTP_OLDVERSION + 1;
11554359Sroberto
11654359Sroberto
11754359Sroberto/*
11854359Sroberto * Format values
11954359Sroberto */
12054359Sroberto#define	PADDING	0
121285612Sdelphij#define	HA	1	/* host address */
122285612Sdelphij#define	NA	2	/* network address */
123285612Sdelphij#define	LP	3	/* leap (print in binary) */
124285612Sdelphij#define	RF	4	/* refid (sometimes string, sometimes not) */
125285612Sdelphij#define	AR	5	/* array of times */
126285612Sdelphij#define FX	6	/* test flags */
127285612Sdelphij#define TS	7	/* l_fp timestamp in hex */
128285612Sdelphij#define	OC	8	/* integer, print in octal */
12954359Sroberto#define	EOV	255	/* end of table */
13054359Sroberto
13154359Sroberto/*
132285612Sdelphij * For the most part ntpq simply displays what ntpd provides in the
133285612Sdelphij * mostly plain-text mode 6 responses.  A few variable names are by
134285612Sdelphij * default "cooked" to provide more human-friendly output.
13554359Sroberto */
136285612Sdelphijconst var_format cookedvars[] = {
137285612Sdelphij	{ "leap",		LP },
138285612Sdelphij	{ "reach",		OC },
139285612Sdelphij	{ "refid",		RF },
140285612Sdelphij	{ "reftime",		TS },
141285612Sdelphij	{ "clock",		TS },
142285612Sdelphij	{ "org",		TS },
143285612Sdelphij	{ "rec",		TS },
144285612Sdelphij	{ "xmt",		TS },
145285612Sdelphij	{ "flash",		FX },
146285612Sdelphij	{ "srcadr",		HA },
147285612Sdelphij	{ "peeradr",		HA },	/* compat with others */
148285612Sdelphij	{ "dstadr",		NA },
149285612Sdelphij	{ "filtdelay",		AR },
150285612Sdelphij	{ "filtoffset",		AR },
151285612Sdelphij	{ "filtdisp",		AR },
152285612Sdelphij	{ "filterror",		AR },	/* compat with others */
15354359Sroberto};
15454359Sroberto
15554359Sroberto
15654359Sroberto
15754359Sroberto/*
15854359Sroberto * flasher bits
15954359Sroberto */
16054359Srobertostatic const char *tstflagnames[] = {
161182007Sroberto	"pkt_dup",		/* TEST1 */
162182007Sroberto	"pkt_bogus",		/* TEST2 */
163285612Sdelphij	"pkt_unsync",		/* TEST3 */
164182007Sroberto	"pkt_denied",		/* TEST4 */
165182007Sroberto	"pkt_auth",		/* TEST5 */
166285612Sdelphij	"pkt_stratum",		/* TEST6 */
167285612Sdelphij	"pkt_header",		/* TEST7 */
168182007Sroberto	"pkt_autokey",		/* TEST8 */
169182007Sroberto	"pkt_crypto",		/* TEST9 */
170182007Sroberto	"peer_stratum",		/* TEST10 */
171182007Sroberto	"peer_dist",		/* TEST11 */
172182007Sroberto	"peer_loop",		/* TEST12 */
173285612Sdelphij	"peer_unreach"		/* TEST13 */
17454359Sroberto};
17554359Sroberto
17654359Sroberto
177285612Sdelphijint		ntpqmain	(int,	char **);
17854359Sroberto/*
17954359Sroberto * Built in command handler declarations
18054359Sroberto */
181285612Sdelphijstatic	int	openhost	(const char *, int);
182285612Sdelphijstatic	void	dump_hex_printable(const void *, size_t);
183285612Sdelphijstatic	int	sendpkt		(void *, size_t);
184293650Sglebiusstatic	int	getresponse	(int, int, u_short *, size_t *, const char **, int);
185293650Sglebiusstatic	int	sendrequest	(int, associd_t, int, size_t, const char *);
186285612Sdelphijstatic	char *	tstflags	(u_long);
187285612Sdelphij#ifndef BUILD_AS_LIB
188285612Sdelphijstatic	void	getcmds		(void);
189285612Sdelphij#ifndef SYS_WINNT
190293650Sglebiusstatic	int	abortcmd	(void);
191285612Sdelphij#endif	/* SYS_WINNT */
192285612Sdelphijstatic	void	docmd		(const char *);
193285612Sdelphijstatic	void	tokenize	(const char *, char **, int *);
194285612Sdelphijstatic	int	getarg		(const char *, int, arg_v *);
195285612Sdelphij#endif	/* BUILD_AS_LIB */
196285612Sdelphijstatic	int	findcmd		(const char *, struct xcmd *,
197285612Sdelphij				 struct xcmd *, struct xcmd **);
198285612Sdelphijstatic	int	rtdatetolfp	(char *, l_fp *);
199330141Sdelphijstatic	int	decodearr	(char *, int *, l_fp *, int);
200285612Sdelphijstatic	void	help		(struct parse *, FILE *);
201285612Sdelphijstatic	int	helpsort	(const void *, const void *);
202285612Sdelphijstatic	void	printusage	(struct xcmd *, FILE *);
203285612Sdelphijstatic	void	timeout		(struct parse *, FILE *);
204285612Sdelphijstatic	void	auth_delay	(struct parse *, FILE *);
205285612Sdelphijstatic	void	host		(struct parse *, FILE *);
206285612Sdelphijstatic	void	ntp_poll	(struct parse *, FILE *);
207285612Sdelphijstatic	void	keyid		(struct parse *, FILE *);
208285612Sdelphijstatic	void	keytype		(struct parse *, FILE *);
209285612Sdelphijstatic	void	passwd		(struct parse *, FILE *);
210285612Sdelphijstatic	void	hostnames	(struct parse *, FILE *);
211285612Sdelphijstatic	void	setdebug	(struct parse *, FILE *);
212285612Sdelphijstatic	void	quit		(struct parse *, FILE *);
213298699Sdelphijstatic	void	showdrefid	(struct parse *, FILE *);
214285612Sdelphijstatic	void	version		(struct parse *, FILE *);
215285612Sdelphijstatic	void	raw		(struct parse *, FILE *);
216285612Sdelphijstatic	void	cooked		(struct parse *, FILE *);
217285612Sdelphijstatic	void	authenticate	(struct parse *, FILE *);
218285612Sdelphijstatic	void	ntpversion	(struct parse *, FILE *);
219338531Sdelphijstatic	void	warning		(const char *, ...) NTP_PRINTF(1, 2);
220338531Sdelphijstatic	void	error		(const char *, ...) NTP_PRINTF(1, 2);
221285612Sdelphijstatic	u_long	getkeyid	(const char *);
222285612Sdelphijstatic	void	atoascii	(const char *, size_t, char *, size_t);
223293650Sglebiusstatic	void	cookedprint	(int, size_t, const char *, int, int, FILE *);
224293650Sglebiusstatic	void	rawprint	(int, size_t, const char *, int, int, FILE *);
225285612Sdelphijstatic	void	startoutput	(void);
226285612Sdelphijstatic	void	output		(FILE *, const char *, const char *);
227285612Sdelphijstatic	void	endoutput	(FILE *);
228285612Sdelphijstatic	void	outputarr	(FILE *, char *, int, l_fp *);
229285612Sdelphijstatic	int	assoccmp	(const void *, const void *);
230338531Sdelphij	u_short	varfmt		(const char *);
231338531Sdelphij	void	ntpq_custom_opt_handler(tOptions *, tOptDesc *);
232338531Sdelphij
233338531Sdelphij#ifndef BUILD_AS_LIB
234338531Sdelphijstatic	char   *list_digest_names(void);
235338531Sdelphijstatic	char   *insert_cmac	(char *list);
236293650Sglebiusstatic	void	on_ctrlc	(void);
237294569Sdelphijstatic	int	my_easprintf	(char**, const char *, ...) NTP_PRINTF(2, 3);
238338531Sdelphij# if defined(OPENSSL) && defined(HAVE_EVP_MD_DO_ALL_SORTED)
239338531Sdelphijstatic	void	list_md_fn	(const EVP_MD *m, const char *from,
240338531Sdelphij				 const char *to, void *arg);
241338531Sdelphij# endif /* defined(OPENSSL) && defined(HAVE_EVP_MD_DO_ALL_SORTED) */
242338531Sdelphij#endif /* !defined(BUILD_AS_LIB) */
243285612Sdelphij
244338531Sdelphij
245330141Sdelphij/* read a character from memory and expand to integer */
246330141Sdelphijstatic inline int
247330141Sdelphijpgetc(
248330141Sdelphij	const char *cp
249330141Sdelphij	)
250330141Sdelphij{
251330141Sdelphij	return (int)*(const unsigned char*)cp;
252330141Sdelphij}
253330141Sdelphij
254330141Sdelphij
25554359Sroberto
25654359Sroberto/*
25754359Sroberto * Built-in commands we understand
25854359Sroberto */
25954359Srobertostruct xcmd builtins[] = {
260182007Sroberto	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
26154359Sroberto	  { "command", "", "", "" },
26254359Sroberto	  "tell the use and syntax of commands" },
263182007Sroberto	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
26454359Sroberto	  { "command", "", "", "" },
26554359Sroberto	  "tell the use and syntax of commands" },
266182007Sroberto	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
26754359Sroberto	  { "msec", "", "", "" },
26854359Sroberto	  "set the primary receive time out" },
269182007Sroberto	{ "delay",	auth_delay,	{ OPT|NTP_INT, NO, NO, NO },
27054359Sroberto	  { "msec", "", "", "" },
27154359Sroberto	  "set the delay added to encryption time stamps" },
272182007Sroberto	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
273132451Sroberto	  { "-4|-6", "hostname", "", "" },
27454359Sroberto	  "specify the host whose NTP server we talk to" },
275182007Sroberto	{ "poll",	ntp_poll,	{ OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
27654359Sroberto	  { "n", "verbose", "", "" },
27754359Sroberto	  "poll an NTP server in client mode `n' times" },
278285612Sdelphij	{ "passwd",	passwd,		{ OPT|NTP_STR, NO, NO, NO },
27954359Sroberto	  { "", "", "", "" },
28054359Sroberto	  "specify a password to use for authenticated requests"},
281182007Sroberto	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
28254359Sroberto	  { "yes|no", "", "", "" },
28354359Sroberto	  "specify whether hostnames or net numbers are printed"},
284182007Sroberto	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
28554359Sroberto	  { "no|more|less", "", "", "" },
28654359Sroberto	  "set/change debugging level" },
28754359Sroberto	{ "quit",	quit,		{ NO, NO, NO, NO },
28854359Sroberto	  { "", "", "", "" },
28954359Sroberto	  "exit ntpq" },
29054359Sroberto	{ "exit",	quit,		{ NO, NO, NO, NO },
29154359Sroberto	  { "", "", "", "" },
29254359Sroberto	  "exit ntpq" },
293182007Sroberto	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
29454359Sroberto	  { "key#", "", "", "" },
29554359Sroberto	  "set keyid to use for authenticated requests" },
296298699Sdelphij	{ "drefid",	showdrefid,	{ OPT|NTP_STR, NO, NO, NO },
297298699Sdelphij	  { "hash|ipv4", "", "", "" },
298298699Sdelphij	  "display refid's as IPv4 or hash" },
29954359Sroberto	{ "version",	version,	{ NO, NO, NO, NO },
30054359Sroberto	  { "", "", "", "" },
30154359Sroberto	  "print version number" },
30254359Sroberto	{ "raw",	raw,		{ NO, NO, NO, NO },
30354359Sroberto	  { "", "", "", "" },
30454359Sroberto	  "do raw mode variable output" },
30554359Sroberto	{ "cooked",	cooked,		{ NO, NO, NO, NO },
30654359Sroberto	  { "", "", "", "" },
30754359Sroberto	  "do cooked mode variable output" },
308182007Sroberto	{ "authenticate", authenticate,	{ OPT|NTP_STR, NO, NO, NO },
30954359Sroberto	  { "yes|no", "", "", "" },
31054359Sroberto	  "always authenticate requests to this server" },
311182007Sroberto	{ "ntpversion",	ntpversion,	{ OPT|NTP_UINT, NO, NO, NO },
31254359Sroberto	  { "version number", "", "", "" },
31354359Sroberto	  "set the NTP version number to use for requests" },
314182007Sroberto	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
315285612Sdelphij	  { "key type %s", "", "", "" },
316285612Sdelphij	  NULL },
31754359Sroberto	{ 0,		0,		{ NO, NO, NO, NO },
31854359Sroberto	  { "", "", "", "" }, "" }
31954359Sroberto};
32054359Sroberto
32154359Sroberto
32254359Sroberto/*
32354359Sroberto * Default values we use.
32454359Sroberto */
325285612Sdelphij#define	DEFHOST		"localhost"	/* default host name */
326285612Sdelphij#define	DEFTIMEOUT	5		/* wait 5 seconds for 1st pkt */
327285612Sdelphij#define	DEFSTIMEOUT	3		/* and 3 more for each additional */
328285612Sdelphij/*
329285612Sdelphij * Requests are automatically retried once, so total timeout with no
330285612Sdelphij * response is a bit over 2 * DEFTIMEOUT, or 10 seconds.  At the other
331285612Sdelphij * extreme, a request eliciting 32 packets of responses each for some
332285612Sdelphij * reason nearly DEFSTIMEOUT seconds after the prior in that series,
333285612Sdelphij * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
334285612Sdelphij * 93 seconds to fail each of two times, or 186 seconds.
335285612Sdelphij * Some commands involve a series of requests, such as "peers" and
336285612Sdelphij * "mrulist", so the cumulative timeouts are even longer for those.
337285612Sdelphij */
33854359Sroberto#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
33954359Sroberto#define	LENHOSTNAME	256		/* host name is 256 characters long */
34054359Sroberto#define	MAXCMDS		100		/* maximum commands on cmd line */
34154359Sroberto#define	MAXHOSTS	200		/* maximum hosts on cmd line */
34254359Sroberto#define	MAXLINE		512		/* maximum line length */
34354359Sroberto#define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
34454359Sroberto#define	MAXVARLEN	256		/* maximum length of a variable name */
345285612Sdelphij#define	MAXVALLEN	2048		/* maximum length of a variable value */
34654359Sroberto#define	MAXOUTLINE	72		/* maximum length of an output line */
347285612Sdelphij#define SCREENWIDTH	76		/* nominal screen width in columns */
34854359Sroberto
34954359Sroberto/*
35054359Sroberto * Some variables used and manipulated locally
35154359Sroberto */
352285612Sdelphijstruct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
353285612Sdelphijstruct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
35454359Srobertol_fp delay_time;				/* delay time */
35554359Srobertochar currenthost[LENHOSTNAME];			/* current host name */
356285612Sdelphijint currenthostisnum;				/* is prior text from IP? */
357285612Sdelphijstruct sockaddr_in hostaddr;			/* host address */
35854359Srobertoint showhostnames = 1;				/* show host names by default */
359285612Sdelphijint wideremote = 0;				/* show wide remote names? */
36054359Sroberto
361132451Srobertoint ai_fam_templ;				/* address family */
362132451Srobertoint ai_fam_default;				/* default address family */
363132451SrobertoSOCKET sockfd;					/* fd socket is opened on */
36454359Srobertoint havehost = 0;				/* set to 1 when host open */
365132451Srobertoint s_port = 0;
36654359Srobertostruct servent *server_entry = NULL;		/* server entry for ntp */
36754359Sroberto
36854359Sroberto
36954359Sroberto/*
37054359Sroberto * Sequence number used for requests.  It is incremented before
37154359Sroberto * it is used.
37254359Sroberto */
37354359Srobertou_short sequence;
37454359Sroberto
37554359Sroberto/*
37654359Sroberto * Holds data returned from queries.  Declare buffer long to be sure of
37754359Sroberto * alignment.
37854359Sroberto */
37954359Sroberto#define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
38054359Srobertolong pktdata[DATASIZE/sizeof(long)];
38154359Sroberto
38254359Sroberto/*
383285612Sdelphij * assoc_cache[] is a dynamic array which allows references to
384285612Sdelphij * associations using &1 ... &N for n associations, avoiding manual
385285612Sdelphij * lookup of the current association IDs for a given ntpd.  It also
386285612Sdelphij * caches the status word for each association, retrieved incidentally.
38754359Sroberto */
388285612Sdelphijstruct association *	assoc_cache;
389285612Sdelphiju_int assoc_cache_slots;/* count of allocated array entries */
390285612Sdelphiju_int numassoc;		/* number of cached associations */
39154359Sroberto
39254359Sroberto/*
39354359Sroberto * For commands typed on the command line (with the -c option)
39454359Sroberto */
395316069Sdelphijsize_t numcmds = 0;
39654359Srobertoconst char *ccmds[MAXCMDS];
39754359Sroberto#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
39854359Sroberto
39954359Sroberto/*
40054359Sroberto * When multiple hosts are specified.
40154359Sroberto */
40254359Sroberto
403285612Sdelphiju_int numhosts;
40454359Sroberto
405285612Sdelphijchost chosts[MAXHOSTS];
406285612Sdelphij#define	ADDHOST(cp)						\
407285612Sdelphij	do {							\
408285612Sdelphij		if (numhosts < MAXHOSTS) {			\
409285612Sdelphij			chosts[numhosts].name = (cp);		\
410285612Sdelphij			chosts[numhosts].fam = ai_fam_templ;	\
411285612Sdelphij			numhosts++;				\
412285612Sdelphij		}						\
413285612Sdelphij	} while (0)
414285612Sdelphij
41554359Sroberto/*
41654359Sroberto * Macro definitions we use
41754359Sroberto */
41854359Sroberto#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
41954359Sroberto#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
42054359Sroberto#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
42154359Sroberto
42254359Sroberto/*
423338531Sdelphij * Jump buffer for longjumping back to the command level.
424338531Sdelphij *
425338531Sdelphij * Since we do this from a signal handler, we use 'sig{set,long}jmp()'
426338531Sdelphij * if available. The signal is blocked by default during the excution of
427338531Sdelphij * a signal handler, and it is unspecified if '{set,long}jmp()' save and
428338531Sdelphij * restore the signal mask. They do on BSD, it depends on the GLIBC
429338531Sdelphij * version on Linux, and the gods know what happens on other OSes...
430338531Sdelphij *
431338531Sdelphij * So we use the 'sig{set,long}jmp()' functions where available, because
432338531Sdelphij * for them the semantics are well-defined. If we have to fall back to
433338531Sdelphij * '{set,long}jmp()', the CTRL-C handling might be a bit erratic.
43454359Sroberto */
435338531Sdelphij#if HAVE_DECL_SIGSETJMP && HAVE_DECL_SIGLONGJMP
436338531Sdelphij# define JMP_BUF	sigjmp_buf
437338531Sdelphij# define SETJMP(x)	sigsetjmp((x), 1)
438338531Sdelphij# define LONGJMP(x, v)	siglongjmp((x),(v))
439338531Sdelphij#else
440338531Sdelphij# define JMP_BUF	jmp_buf
441338531Sdelphij# define SETJMP(x)	setjmp((x))
442338531Sdelphij# define LONGJMP(x, v)	longjmp((x),(v))
443338531Sdelphij#endif
444338531Sdelphijstatic	JMP_BUF		interrupt_buf;
445338531Sdelphijstatic	volatile int	jump = 0;
44654359Sroberto
44754359Sroberto/*
44854359Sroberto * Points at file being currently printed into
44954359Sroberto */
450338531SdelphijFILE *current_output = NULL;
45154359Sroberto
45254359Sroberto/*
45354359Sroberto * Command table imported from ntpdc_ops.c
45454359Sroberto */
45554359Srobertoextern struct xcmd opcmds[];
45654359Sroberto
457289997Sglebiuschar const *progname;
45854359Sroberto
45954359Sroberto#ifdef NO_MAIN_ALLOWED
460285612Sdelphij#ifndef BUILD_AS_LIB
46154359SrobertoCALL(ntpq,"ntpq",ntpqmain);
46254359Sroberto
46354359Srobertovoid clear_globals(void)
46454359Sroberto{
465285612Sdelphij	extern int ntp_optind;
466285612Sdelphij	showhostnames = 0;	/* don'tshow host names by default */
467285612Sdelphij	ntp_optind = 0;
468285612Sdelphij	server_entry = NULL;	/* server entry for ntp */
469285612Sdelphij	havehost = 0;		/* set to 1 when host open */
470285612Sdelphij	numassoc = 0;		/* number of cached associations */
471285612Sdelphij	numcmds = 0;
472285612Sdelphij	numhosts = 0;
47354359Sroberto}
474285612Sdelphij#endif /* !BUILD_AS_LIB */
475285612Sdelphij#endif /* NO_MAIN_ALLOWED */
47654359Sroberto
47754359Sroberto/*
47854359Sroberto * main - parse arguments and handle options
47954359Sroberto */
48054359Sroberto#ifndef NO_MAIN_ALLOWED
48154359Srobertoint
48254359Srobertomain(
48354359Sroberto	int argc,
48454359Sroberto	char *argv[]
48554359Sroberto	)
48654359Sroberto{
48754359Sroberto	return ntpqmain(argc, argv);
48854359Sroberto}
48954359Sroberto#endif
49054359Sroberto
491330141Sdelphij
492285612Sdelphij#ifndef BUILD_AS_LIB
49354359Srobertoint
49454359Srobertontpqmain(
49554359Sroberto	int argc,
49654359Sroberto	char *argv[]
49754359Sroberto	)
49854359Sroberto{
499285612Sdelphij	u_int ihost;
500316069Sdelphij	size_t icmd;
50154359Sroberto
502285612Sdelphij
503182007Sroberto#ifdef SYS_VXWORKS
504182007Sroberto	clear_globals();
505182007Sroberto	taskPrioritySet(taskIdSelf(), 100 );
50654359Sroberto#endif
507182007Sroberto
50854359Sroberto	delay_time.l_ui = 0;
50954359Sroberto	delay_time.l_uf = DEFDELAY;
51054359Sroberto
511285612Sdelphij	init_lib();	/* sets up ipv4_works, ipv6_works */
512285612Sdelphij	ssl_applink();
513285612Sdelphij	init_auth();
514285612Sdelphij
515285612Sdelphij	/* Check to see if we have IPv6. Otherwise default to IPv4 */
516285612Sdelphij	if (!ipv6_works)
517285612Sdelphij		ai_fam_default = AF_INET;
518285612Sdelphij
519285612Sdelphij	/* Fixup keytype's help based on available digest names */
520285612Sdelphij
521132451Sroberto	{
522285612Sdelphij	    char *list;
523294569Sdelphij	    char *msg;
524132451Sroberto
525285612Sdelphij	    list = list_digest_names();
526330141Sdelphij
527330141Sdelphij	    for (icmd = 0; icmd < sizeof(builtins)/sizeof(*builtins); icmd++) {
528330141Sdelphij		if (strcmp("keytype", builtins[icmd].keyword) == 0) {
529285612Sdelphij		    break;
530330141Sdelphij		}
531285612Sdelphij	    }
532285612Sdelphij
533285612Sdelphij	    /* CID: 1295478 */
534285612Sdelphij	    /* This should only "trip" if "keytype" is removed from builtins */
535330141Sdelphij	    INSIST(icmd < sizeof(builtins)/sizeof(*builtins));
536285612Sdelphij
537285612Sdelphij#ifdef OPENSSL
538285612Sdelphij	    builtins[icmd].desc[0] = "digest-name";
539294569Sdelphij	    my_easprintf(&msg,
540294569Sdelphij			 "set key type to use for authenticated requests, one of:%s",
541294569Sdelphij			 list);
542285612Sdelphij#else
543285612Sdelphij	    builtins[icmd].desc[0] = "md5";
544294569Sdelphij	    my_easprintf(&msg,
545294569Sdelphij			 "set key type to use for authenticated requests (%s)",
546294569Sdelphij			 list);
547285612Sdelphij#endif
548285612Sdelphij	    builtins[icmd].comment = msg;
549285612Sdelphij	    free(list);
550132451Sroberto	}
551132451Sroberto
55254359Sroberto	progname = argv[0];
553182007Sroberto
554182007Sroberto	{
555285612Sdelphij		int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
556182007Sroberto		argc -= optct;
557182007Sroberto		argv += optct;
558182007Sroberto	}
559182007Sroberto
560285612Sdelphij	/*
561285612Sdelphij	 * Process options other than -c and -p, which are specially
562285612Sdelphij	 * handled by ntpq_custom_opt_handler().
563285612Sdelphij	 */
564285612Sdelphij
565285612Sdelphij	debug = OPT_VALUE_SET_DEBUG_LEVEL;
566285612Sdelphij
567285612Sdelphij	if (HAVE_OPT(IPV4))
568182007Sroberto		ai_fam_templ = AF_INET;
569285612Sdelphij	else if (HAVE_OPT(IPV6))
570182007Sroberto		ai_fam_templ = AF_INET6;
571285612Sdelphij	else
572182007Sroberto		ai_fam_templ = ai_fam_default;
573182007Sroberto
574285612Sdelphij	if (HAVE_OPT(INTERACTIVE))
575182007Sroberto		interactive = 1;
576182007Sroberto
577285612Sdelphij	if (HAVE_OPT(NUMERIC))
578182007Sroberto		showhostnames = 0;
579182007Sroberto
580285612Sdelphij	if (HAVE_OPT(WIDE))
581285612Sdelphij		wideremote = 1;
582182007Sroberto
583285612Sdelphij	old_rv = HAVE_OPT(OLD_RV);
584285612Sdelphij
585298699Sdelphij	drefid = OPT_VALUE_REFID;
586298699Sdelphij
587285612Sdelphij	if (0 == argc) {
58854359Sroberto		ADDHOST(DEFHOST);
58954359Sroberto	} else {
590285612Sdelphij		for (ihost = 0; ihost < (u_int)argc; ihost++) {
591285612Sdelphij			if ('-' == *argv[ihost]) {
592285612Sdelphij				//
593285612Sdelphij				// If I really cared I'd also check:
594285612Sdelphij				// 0 == argv[ihost][2]
595285612Sdelphij				//
596285612Sdelphij				// and there are other cases as well...
597285612Sdelphij				//
598285612Sdelphij				if ('4' == argv[ihost][1]) {
599285612Sdelphij					ai_fam_templ = AF_INET;
600285612Sdelphij					continue;
601285612Sdelphij				} else if ('6' == argv[ihost][1]) {
602285612Sdelphij					ai_fam_templ = AF_INET6;
603285612Sdelphij					continue;
604285612Sdelphij				} else {
605285612Sdelphij					// XXX Throw a usage error
606285612Sdelphij				}
607285612Sdelphij			}
608285612Sdelphij			ADDHOST(argv[ihost]);
609285612Sdelphij		}
61054359Sroberto	}
61154359Sroberto
61254359Sroberto	if (numcmds == 0 && interactive == 0
61354359Sroberto	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
61454359Sroberto		interactive = 1;
61554359Sroberto	}
61654359Sroberto
617293650Sglebius	set_ctrl_c_hook(on_ctrlc);
61854359Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
61954359Sroberto	if (interactive)
620293650Sglebius		push_ctrl_c_handler(abortcmd);
62154359Sroberto#endif /* SYS_WINNT */
62254359Sroberto
62354359Sroberto	if (numcmds == 0) {
624285612Sdelphij		(void) openhost(chosts[0].name, chosts[0].fam);
62554359Sroberto		getcmds();
62654359Sroberto	} else {
62754359Sroberto		for (ihost = 0; ihost < numhosts; ihost++) {
628330141Sdelphij			if (openhost(chosts[ihost].name, chosts[ihost].fam)) {
629338531Sdelphij				if (ihost && current_output)
630330141Sdelphij					fputc('\n', current_output);
631330141Sdelphij				for (icmd = 0; icmd < numcmds; icmd++) {
632338531Sdelphij					if (icmd && current_output)
633330141Sdelphij						fputc('\n', current_output);
634285612Sdelphij					docmd(ccmds[icmd]);
635330141Sdelphij				}
636330141Sdelphij			}
63754359Sroberto		}
63854359Sroberto	}
63954359Sroberto#ifdef SYS_WINNT
64054359Sroberto	WSACleanup();
64154359Sroberto#endif /* SYS_WINNT */
64254359Sroberto	return 0;
64354359Sroberto}
644285612Sdelphij#endif /* !BUILD_AS_LIB */
64554359Sroberto
64654359Sroberto/*
64754359Sroberto * openhost - open a socket to a host
64854359Sroberto */
649285612Sdelphijstatic	int
65054359Srobertoopenhost(
651285612Sdelphij	const char *hname,
652285612Sdelphij	int	    fam
65354359Sroberto	)
65454359Sroberto{
655285612Sdelphij	const char svc[] = "ntp";
65654359Sroberto	char temphost[LENHOSTNAME];
657338531Sdelphij	int a_info;
658285612Sdelphij	struct addrinfo hints, *ai;
659285612Sdelphij	sockaddr_u addr;
660285612Sdelphij	size_t octets;
661338531Sdelphij	const char *cp;
662132451Sroberto	char name[LENHOSTNAME];
66354359Sroberto
664132451Sroberto	/*
665132451Sroberto	 * We need to get by the [] if they were entered
666132451Sroberto	 */
667338531Sdelphij	if (*hname == '[') {
668338531Sdelphij		cp = strchr(hname + 1, ']');
669338531Sdelphij		if (!cp || (octets = (size_t)(cp - hname) - 1) >= sizeof(name)) {
670338531Sdelphij			errno = EINVAL;
671338531Sdelphij			warning("%s", "bad hostname/address");
672285612Sdelphij			return 0;
673285612Sdelphij		}
674338531Sdelphij		memcpy(name, hname + 1, octets);
675338531Sdelphij		name[octets] = '\0';
676338531Sdelphij		hname = name;
67754359Sroberto	}
67854359Sroberto
679132451Sroberto	/*
680132451Sroberto	 * First try to resolve it as an ip address and if that fails,
681132451Sroberto	 * do a fullblown (dns) lookup. That way we only use the dns
682132451Sroberto	 * when it is needed and work around some implementations that
683132451Sroberto	 * will return an "IPv4-mapped IPv6 address" address if you
684132451Sroberto	 * give it an IPv4 address to lookup.
685132451Sroberto	 */
686285612Sdelphij	ZERO(hints);
687285612Sdelphij	hints.ai_family = fam;
688132451Sroberto	hints.ai_protocol = IPPROTO_UDP;
689132451Sroberto	hints.ai_socktype = SOCK_DGRAM;
690285612Sdelphij	hints.ai_flags = Z_AI_NUMERICHOST;
691285612Sdelphij	ai = NULL;
692132451Sroberto
693285612Sdelphij	a_info = getaddrinfo(hname, svc, &hints, &ai);
694182007Sroberto	if (a_info == EAI_NONAME
695182007Sroberto#ifdef EAI_NODATA
696182007Sroberto	    || a_info == EAI_NODATA
697182007Sroberto#endif
698182007Sroberto	   ) {
699132451Sroberto		hints.ai_flags = AI_CANONNAME;
700132451Sroberto#ifdef AI_ADDRCONFIG
701132451Sroberto		hints.ai_flags |= AI_ADDRCONFIG;
702132451Sroberto#endif
703285612Sdelphij		a_info = getaddrinfo(hname, svc, &hints, &ai);
704132451Sroberto	}
705285612Sdelphij#ifdef AI_ADDRCONFIG
706132451Sroberto	/* Some older implementations don't like AI_ADDRCONFIG. */
707132451Sroberto	if (a_info == EAI_BADFLAGS) {
708285612Sdelphij		hints.ai_flags &= ~AI_ADDRCONFIG;
709285612Sdelphij		a_info = getaddrinfo(hname, svc, &hints, &ai);
710132451Sroberto	}
711285612Sdelphij#endif
712132451Sroberto	if (a_info != 0) {
713285612Sdelphij		fprintf(stderr, "%s\n", gai_strerror(a_info));
714132451Sroberto		return 0;
715132451Sroberto	}
716132451Sroberto
717285612Sdelphij	INSIST(ai != NULL);
718285612Sdelphij	ZERO(addr);
719285612Sdelphij	octets = min(sizeof(addr), ai->ai_addrlen);
720285612Sdelphij	memcpy(&addr, ai->ai_addr, octets);
721285612Sdelphij
722132451Sroberto	if (ai->ai_canonname == NULL) {
723285612Sdelphij		strlcpy(temphost, stoa(&addr), sizeof(temphost));
724285612Sdelphij		currenthostisnum = TRUE;
725132451Sroberto	} else {
726285612Sdelphij		strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
727285612Sdelphij		currenthostisnum = FALSE;
728132451Sroberto	}
729132451Sroberto
73054359Sroberto	if (debug > 2)
731285612Sdelphij		printf("Opening host %s (%s)\n",
732285612Sdelphij			temphost,
733285612Sdelphij			(ai->ai_family == AF_INET)
734285612Sdelphij			? "AF_INET"
735285612Sdelphij			: (ai->ai_family == AF_INET6)
736285612Sdelphij			  ? "AF_INET6"
737285612Sdelphij			  : "AF-???"
738285612Sdelphij			);
73954359Sroberto
74054359Sroberto	if (havehost == 1) {
74154359Sroberto		if (debug > 2)
742285612Sdelphij			printf("Closing old host %s\n", currenthost);
743285612Sdelphij		closesocket(sockfd);
74454359Sroberto		havehost = 0;
74554359Sroberto	}
746285612Sdelphij	strlcpy(currenthost, temphost, sizeof(currenthost));
74754359Sroberto
748132451Sroberto	/* port maps to the same location in both families */
749285612Sdelphij	s_port = NSRCPORT(&addr);
750132451Sroberto#ifdef SYS_VXWORKS
751132451Sroberto	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
752132451Sroberto	if (ai->ai_family == AF_INET)
753132451Sroberto		*(struct sockaddr_in *)&hostaddr=
754132451Sroberto			*((struct sockaddr_in *)ai->ai_addr);
755132451Sroberto	else
756132451Sroberto		*(struct sockaddr_in6 *)&hostaddr=
757132451Sroberto			*((struct sockaddr_in6 *)ai->ai_addr);
758132451Sroberto#endif /* SYS_VXWORKS */
75954359Sroberto
76054359Sroberto#ifdef SYS_WINNT
76154359Sroberto	{
76254359Sroberto		int optionValue = SO_SYNCHRONOUS_NONALERT;
76354359Sroberto		int err;
764182007Sroberto
765285612Sdelphij		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
766330141Sdelphij				 (void *)&optionValue, sizeof(optionValue));
767285612Sdelphij		if (err) {
768285612Sdelphij			mfprintf(stderr,
769285612Sdelphij				 "setsockopt(SO_SYNCHRONOUS_NONALERT)"
770285612Sdelphij				 " error: %m\n");
771285612Sdelphij			freeaddrinfo(ai);
77254359Sroberto			exit(1);
77354359Sroberto		}
77454359Sroberto	}
775132451Sroberto#endif /* SYS_WINNT */
77654359Sroberto
777285612Sdelphij	sockfd = socket(ai->ai_family, ai->ai_socktype,
778285612Sdelphij			ai->ai_protocol);
77954359Sroberto	if (sockfd == INVALID_SOCKET) {
780285612Sdelphij		error("socket");
781285612Sdelphij		freeaddrinfo(ai);
782285612Sdelphij		return 0;
78354359Sroberto	}
78454359Sroberto
785285612Sdelphij
78654359Sroberto#ifdef NEED_RCVBUF_SLOP
78754359Sroberto# ifdef SO_RCVBUF
78854359Sroberto	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
78954359Sroberto	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
790330141Sdelphij		       (void *)&rbufsize, sizeof(int)) == -1)
791285612Sdelphij		error("setsockopt");
79254359Sroberto	}
79354359Sroberto# endif
79454359Sroberto#endif
79554359Sroberto
796285612Sdelphij	if
797132451Sroberto#ifdef SYS_VXWORKS
798285612Sdelphij	   (connect(sockfd, (struct sockaddr *)&hostaddr,
79954359Sroberto		    sizeof(hostaddr)) == -1)
800132451Sroberto#else
801285612Sdelphij	   (connect(sockfd, (struct sockaddr *)ai->ai_addr,
802293650Sglebius		ai->ai_addrlen) == -1)
803132451Sroberto#endif /* SYS_VXWORKS */
804293650Sglebius	{
805285612Sdelphij		error("connect");
806132451Sroberto		freeaddrinfo(ai);
807285612Sdelphij		return 0;
808285612Sdelphij	}
809285612Sdelphij	freeaddrinfo(ai);
81054359Sroberto	havehost = 1;
811285612Sdelphij	numassoc = 0;
812285612Sdelphij
81354359Sroberto	return 1;
81454359Sroberto}
81554359Sroberto
81654359Sroberto
817285612Sdelphijstatic void
818285612Sdelphijdump_hex_printable(
819285612Sdelphij	const void *	data,
820285612Sdelphij	size_t		len
821285612Sdelphij	)
822285612Sdelphij{
823316069Sdelphij	/* every line shows at most 16 bytes, so we need a buffer of
824316069Sdelphij	 *   4 * 16 (2 xdigits, 1 char, one sep for xdigits)
825316069Sdelphij	 * + 2 * 1  (block separators)
826316069Sdelphij	 * + <LF> + <NUL>
827316069Sdelphij	 *---------------
828316069Sdelphij	 *  68 bytes
829316069Sdelphij	 */
830316069Sdelphij	static const char s_xdig[16] = "0123456789ABCDEF";
831285612Sdelphij
832316069Sdelphij	char lbuf[68];
833316069Sdelphij	int  ch, rowlen;
834316069Sdelphij	const u_char * cdata = data;
835316069Sdelphij	char *xptr, *pptr;
836316069Sdelphij
837316069Sdelphij	while (len) {
838316069Sdelphij		memset(lbuf, ' ', sizeof(lbuf));
839316069Sdelphij		xptr = lbuf;
840316069Sdelphij		pptr = lbuf + 3*16 + 2;
841316069Sdelphij
842316069Sdelphij		rowlen = (len > 16) ? 16 : (int)len;
843285612Sdelphij		len -= rowlen;
844316069Sdelphij
845316069Sdelphij		do {
846316069Sdelphij			ch = *cdata++;
847316069Sdelphij
848316069Sdelphij			*xptr++ = s_xdig[ch >> 4  ];
849316069Sdelphij			*xptr++ = s_xdig[ch & 0x0F];
850316069Sdelphij			if (++xptr == lbuf + 3*8)
851316069Sdelphij				++xptr;
852316069Sdelphij
853316069Sdelphij			*pptr++ = isprint(ch) ? (char)ch : '.';
854316069Sdelphij		} while (--rowlen);
855316069Sdelphij
856316069Sdelphij		*pptr++ = '\n';
857316069Sdelphij		*pptr   = '\0';
858316069Sdelphij		fputs(lbuf, stdout);
859285612Sdelphij	}
860285612Sdelphij}
861285612Sdelphij
862285612Sdelphij
86354359Sroberto/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
86454359Sroberto/*
86554359Sroberto * sendpkt - send a packet to the remote host
86654359Sroberto */
86754359Srobertostatic int
86854359Srobertosendpkt(
869285612Sdelphij	void *	xdata,
870285612Sdelphij	size_t	xdatalen
87154359Sroberto	)
87254359Sroberto{
87354359Sroberto	if (debug >= 3)
874285612Sdelphij		printf("Sending %zu octets\n", xdatalen);
87554359Sroberto
876293650Sglebius	if (send(sockfd, xdata, xdatalen, 0) == -1) {
877285612Sdelphij		warning("write to %s failed", currenthost);
87854359Sroberto		return -1;
87954359Sroberto	}
88054359Sroberto
88154359Sroberto	if (debug >= 4) {
882285612Sdelphij		printf("Request packet:\n");
883285612Sdelphij		dump_hex_printable(xdata, xdatalen);
88454359Sroberto	}
88554359Sroberto	return 0;
88654359Sroberto}
88754359Sroberto
88854359Sroberto/*
88954359Sroberto * getresponse - get a (series of) response packet(s) and return the data
89054359Sroberto */
89154359Srobertostatic int
89254359Srobertogetresponse(
89354359Sroberto	int opcode,
89454359Sroberto	int associd,
89554359Sroberto	u_short *rstatus,
896293650Sglebius	size_t *rsize,
897285612Sdelphij	const char **rdata,
89854359Sroberto	int timeo
89954359Sroberto	)
90054359Sroberto{
90154359Sroberto	struct ntp_control rpkt;
902285612Sdelphij	struct sock_timeval tvo;
90354359Sroberto	u_short offsets[MAXFRAGS+1];
90454359Sroberto	u_short counts[MAXFRAGS+1];
90554359Sroberto	u_short offset;
90654359Sroberto	u_short count;
907285612Sdelphij	size_t numfrags;
908285612Sdelphij	size_t f;
909285612Sdelphij	size_t ff;
91054359Sroberto	int seenlastfrag;
911285612Sdelphij	int shouldbesize;
91254359Sroberto	fd_set fds;
91354359Sroberto	int n;
914285612Sdelphij	int errcode;
915294569Sdelphij	/* absolute timeout checks. Not 'time_t' by intention! */
916294569Sdelphij	uint32_t tobase;	/* base value for timeout */
917294569Sdelphij	uint32_t tospan;	/* timeout span (max delay) */
918294569Sdelphij	uint32_t todiff;	/* current delay */
91954359Sroberto
920316069Sdelphij	memset(offsets, 0, sizeof(offsets));
921316069Sdelphij	memset(counts , 0, sizeof(counts ));
922316069Sdelphij
92354359Sroberto	/*
92454359Sroberto	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
92554359Sroberto	 * back in response to the request.  We peel the data out of
92654359Sroberto	 * each packet and collect it in one long block.  When the last
92754359Sroberto	 * packet in the sequence is received we'll know how much data we
92854359Sroberto	 * should have had.  Note we use one long time out, should reconsider.
92954359Sroberto	 */
93054359Sroberto	*rsize = 0;
93154359Sroberto	if (rstatus)
932285612Sdelphij		*rstatus = 0;
93354359Sroberto	*rdata = (char *)pktdata;
93454359Sroberto
93554359Sroberto	numfrags = 0;
93654359Sroberto	seenlastfrag = 0;
93754359Sroberto
938294569Sdelphij	tobase = (uint32_t)time(NULL);
939294569Sdelphij
94054359Sroberto	FD_ZERO(&fds);
94154359Sroberto
942285612Sdelphij	/*
943285612Sdelphij	 * Loop until we have an error or a complete response.  Nearly all
944285612Sdelphij	 * code paths to loop again use continue.
945285612Sdelphij	 */
946285612Sdelphij	for (;;) {
94754359Sroberto
948285612Sdelphij		if (numfrags == 0)
949285612Sdelphij			tvo = tvout;
950285612Sdelphij		else
951285612Sdelphij			tvo = tvsout;
952294569Sdelphij		tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
95354359Sroberto
954285612Sdelphij		FD_SET(sockfd, &fds);
955293650Sglebius		n = select(sockfd+1, &fds, NULL, NULL, &tvo);
956285612Sdelphij		if (n == -1) {
957294569Sdelphij#if !defined(SYS_WINNT) && defined(EINTR)
958294569Sdelphij			/* Windows does not know about EINTR (until very
959294569Sdelphij			 * recently) and the handling of console events
960294569Sdelphij			 * is *very* different from POSIX/UNIX signal
961294569Sdelphij			 * handling anyway.
962294569Sdelphij			 *
963294569Sdelphij			 * Under non-windows targets we map EINTR as
964294569Sdelphij			 * 'last packet was received' and try to exit
965294569Sdelphij			 * the receive sequence.
966294569Sdelphij			 */
967294569Sdelphij			if (errno == EINTR) {
968294569Sdelphij				seenlastfrag = 1;
969294569Sdelphij				goto maybe_final;
970294569Sdelphij			}
971294569Sdelphij#endif
972285612Sdelphij			warning("select fails");
973285612Sdelphij			return -1;
974285612Sdelphij		}
975294569Sdelphij
976294569Sdelphij		/*
977294569Sdelphij		 * Check if this is already too late. Trash the data and
978294569Sdelphij		 * fake a timeout if this is so.
979294569Sdelphij		 */
980294569Sdelphij		todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
981294569Sdelphij		if ((n > 0) && (todiff > tospan)) {
982294569Sdelphij			n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
983316069Sdelphij			n -= n; /* faked timeout return from 'select()',
984316069Sdelphij				 * execute RMW cycle on 'n'
985316069Sdelphij				 */
986294569Sdelphij		}
987294569Sdelphij
988316069Sdelphij		if (n <= 0) {
989285612Sdelphij			/*
990285612Sdelphij			 * Timed out.  Return what we have
991285612Sdelphij			 */
992285612Sdelphij			if (numfrags == 0) {
993285612Sdelphij				if (timeo)
994285612Sdelphij					fprintf(stderr,
995285612Sdelphij						"%s: timed out, nothing received\n",
996285612Sdelphij						currenthost);
997285612Sdelphij				return ERR_TIMEOUT;
998285612Sdelphij			}
99954359Sroberto			if (timeo)
1000285612Sdelphij				fprintf(stderr,
1001285612Sdelphij					"%s: timed out with incomplete data\n",
1002285612Sdelphij					currenthost);
100354359Sroberto			if (debug) {
1004285612Sdelphij				fprintf(stderr,
1005285612Sdelphij					"ERR_INCOMPLETE: Received fragments:\n");
1006285612Sdelphij				for (f = 0; f < numfrags; f++)
1007285612Sdelphij					fprintf(stderr,
1008285612Sdelphij						"%2u: %5d %5d\t%3d octets\n",
1009285612Sdelphij						(u_int)f, offsets[f],
1010285612Sdelphij						offsets[f] +
1011285612Sdelphij						counts[f],
1012285612Sdelphij						counts[f]);
1013285612Sdelphij				fprintf(stderr,
1014285612Sdelphij					"last fragment %sreceived\n",
1015285612Sdelphij					(seenlastfrag)
1016285612Sdelphij					    ? ""
1017285612Sdelphij					    : "not ");
101854359Sroberto			}
101954359Sroberto			return ERR_INCOMPLETE;
102054359Sroberto		}
102154359Sroberto
1022285612Sdelphij		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
1023316069Sdelphij		if (n < 0) {
1024285612Sdelphij			warning("read");
1025285612Sdelphij			return -1;
1026285612Sdelphij		}
102754359Sroberto
1028285612Sdelphij		if (debug >= 4) {
1029285612Sdelphij			printf("Response packet:\n");
1030285612Sdelphij			dump_hex_printable(&rpkt, n);
1031285612Sdelphij		}
103254359Sroberto
1033285612Sdelphij		/*
1034285612Sdelphij		 * Check for format errors.  Bug proofing.
1035285612Sdelphij		 */
1036285612Sdelphij		if (n < (int)CTL_HEADER_LEN) {
1037285612Sdelphij			if (debug)
1038285612Sdelphij				printf("Short (%d byte) packet received\n", n);
1039285612Sdelphij			continue;
104054359Sroberto		}
1041285612Sdelphij		if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
1042285612Sdelphij		    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
1043285612Sdelphij			if (debug)
1044285612Sdelphij				printf("Packet received with version %d\n",
1045285612Sdelphij				       PKT_VERSION(rpkt.li_vn_mode));
1046285612Sdelphij			continue;
1047285612Sdelphij		}
1048285612Sdelphij		if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
1049285612Sdelphij			if (debug)
1050285612Sdelphij				printf("Packet received with mode %d\n",
1051285612Sdelphij				       PKT_MODE(rpkt.li_vn_mode));
1052285612Sdelphij			continue;
1053285612Sdelphij		}
1054285612Sdelphij		if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
1055285612Sdelphij			if (debug)
1056285612Sdelphij				printf("Received request packet, wanted response\n");
1057285612Sdelphij			continue;
1058285612Sdelphij		}
105954359Sroberto
1060285612Sdelphij		/*
1061285612Sdelphij		 * Check opcode and sequence number for a match.
1062285612Sdelphij		 * Could be old data getting to us.
1063285612Sdelphij		 */
1064285612Sdelphij		if (ntohs(rpkt.sequence) != sequence) {
1065285612Sdelphij			if (debug)
1066285612Sdelphij				printf("Received sequnce number %d, wanted %d\n",
1067285612Sdelphij				       ntohs(rpkt.sequence), sequence);
1068285612Sdelphij			continue;
1069285612Sdelphij		}
1070285612Sdelphij		if (CTL_OP(rpkt.r_m_e_op) != opcode) {
1071285612Sdelphij			if (debug)
1072285612Sdelphij			    printf(
1073285612Sdelphij				    "Received opcode %d, wanted %d (sequence number okay)\n",
1074285612Sdelphij				    CTL_OP(rpkt.r_m_e_op), opcode);
1075285612Sdelphij			continue;
1076285612Sdelphij		}
107754359Sroberto
1078285612Sdelphij		/*
1079285612Sdelphij		 * Check the error code.  If non-zero, return it.
1080285612Sdelphij		 */
1081285612Sdelphij		if (CTL_ISERROR(rpkt.r_m_e_op)) {
1082285612Sdelphij			errcode = (ntohs(rpkt.status) >> 8) & 0xff;
1083285612Sdelphij			if (CTL_ISMORE(rpkt.r_m_e_op))
1084285612Sdelphij				TRACE(1, ("Error code %d received on not-final packet\n",
1085285612Sdelphij					  errcode));
1086285612Sdelphij			if (errcode == CERR_UNSPEC)
1087285612Sdelphij				return ERR_UNSPEC;
1088285612Sdelphij			return errcode;
108954359Sroberto		}
109054359Sroberto
109154359Sroberto		/*
1092285612Sdelphij		 * Check the association ID to make sure it matches what
1093285612Sdelphij		 * we sent.
109454359Sroberto		 */
1095285612Sdelphij		if (ntohs(rpkt.associd) != associd) {
1096285612Sdelphij			TRACE(1, ("Association ID %d doesn't match expected %d\n",
1097285612Sdelphij				  ntohs(rpkt.associd), associd));
1098285612Sdelphij			/*
1099285612Sdelphij			 * Hack for silly fuzzballs which, at the time of writing,
1100285612Sdelphij			 * return an assID of sys.peer when queried for system variables.
1101285612Sdelphij			 */
110254359Sroberto#ifdef notdef
1103285612Sdelphij			continue;
110454359Sroberto#endif
1105285612Sdelphij		}
110654359Sroberto
1107285612Sdelphij		/*
1108285612Sdelphij		 * Collect offset and count.  Make sure they make sense.
1109285612Sdelphij		 */
1110285612Sdelphij		offset = ntohs(rpkt.offset);
1111285612Sdelphij		count = ntohs(rpkt.count);
111254359Sroberto
111354359Sroberto		/*
1114285612Sdelphij		 * validate received payload size is padded to next 32-bit
1115285612Sdelphij		 * boundary and no smaller than claimed by rpkt.count
111654359Sroberto		 */
1117285612Sdelphij		if (n & 0x3) {
1118285612Sdelphij			TRACE(1, ("Response packet not padded, size = %d\n",
1119285612Sdelphij				  n));
1120285612Sdelphij			continue;
1121285612Sdelphij		}
112254359Sroberto
1123285612Sdelphij		shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
112454359Sroberto
1125285612Sdelphij		if (n < shouldbesize) {
1126285612Sdelphij			printf("Response packet claims %u octets payload, above %ld received\n",
1127301256Sdelphij			       count, (long)(n - CTL_HEADER_LEN));
1128285612Sdelphij			return ERR_INCOMPLETE;
1129285612Sdelphij		}
1130285612Sdelphij
1131285612Sdelphij		if (debug >= 3 && shouldbesize > n) {
1132285612Sdelphij			u_int32 key;
1133285612Sdelphij			u_int32 *lpkt;
1134285612Sdelphij			int maclen;
1135285612Sdelphij
1136285612Sdelphij			/*
1137285612Sdelphij			 * Usually we ignore authentication, but for debugging purposes
1138285612Sdelphij			 * we watch it here.
1139285612Sdelphij			 */
1140285612Sdelphij			/* round to 8 octet boundary */
1141285612Sdelphij			shouldbesize = (shouldbesize + 7) & ~7;
1142285612Sdelphij
1143285612Sdelphij			maclen = n - shouldbesize;
1144285612Sdelphij			if (maclen >= (int)MIN_MAC_LEN) {
1145285612Sdelphij				printf(
1146285612Sdelphij					"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1147285612Sdelphij					n, shouldbesize, maclen);
1148285612Sdelphij				lpkt = (u_int32 *)&rpkt;
1149285612Sdelphij				printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1150285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1151285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1152285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1153285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1154285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1155285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1156285612Sdelphij				key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1157285612Sdelphij				printf("Authenticated with keyid %lu\n", (u_long)key);
1158285612Sdelphij				if (key != 0 && key != info_auth_keyid) {
1159285612Sdelphij					printf("We don't know that key\n");
116054359Sroberto				} else {
1161285612Sdelphij					if (authdecrypt(key, (u_int32 *)&rpkt,
1162285612Sdelphij					    n - maclen, maclen)) {
1163285612Sdelphij						printf("Auth okay!\n");
1164285612Sdelphij					} else {
1165285612Sdelphij						printf("Auth failed!\n");
1166285612Sdelphij					}
116754359Sroberto				}
116854359Sroberto			}
116954359Sroberto		}
117054359Sroberto
1171285612Sdelphij		TRACE(2, ("Got packet, size = %d\n", n));
1172285612Sdelphij		if (count > (n - CTL_HEADER_LEN)) {
1173285612Sdelphij			TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
1174285612Sdelphij				  count, (long)n - CTL_HEADER_LEN));
1175285612Sdelphij			continue;
1176285612Sdelphij		}
1177285612Sdelphij		if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1178285612Sdelphij			TRACE(1, ("Received count of 0 in non-final fragment\n"));
1179285612Sdelphij			continue;
1180285612Sdelphij		}
1181285612Sdelphij		if (offset + count > sizeof(pktdata)) {
1182285612Sdelphij			TRACE(1, ("Offset %u, count %u, too big for buffer\n",
1183285612Sdelphij				  offset, count));
1184285612Sdelphij			return ERR_TOOMUCH;
1185285612Sdelphij		}
1186285612Sdelphij		if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1187285612Sdelphij			TRACE(1, ("Received second last fragment packet\n"));
1188285612Sdelphij			continue;
1189285612Sdelphij		}
119054359Sroberto
1191285612Sdelphij		/*
1192285612Sdelphij		 * So far, so good.  Record this fragment, making sure it doesn't
1193285612Sdelphij		 * overlap anything.
1194285612Sdelphij		 */
1195285612Sdelphij		TRACE(2, ("Packet okay\n"));
119654359Sroberto
1197285612Sdelphij		if (numfrags > (MAXFRAGS - 1)) {
1198285612Sdelphij			TRACE(2, ("Number of fragments exceeds maximum %d\n",
1199285612Sdelphij				  MAXFRAGS - 1));
1200285612Sdelphij			return ERR_TOOMUCH;
120154359Sroberto		}
120254359Sroberto
1203285612Sdelphij		/*
1204285612Sdelphij		 * Find the position for the fragment relative to any
1205285612Sdelphij		 * previously received.
1206285612Sdelphij		 */
1207285612Sdelphij		for (f = 0;
1208285612Sdelphij		     f < numfrags && offsets[f] < offset;
1209285612Sdelphij		     f++) {
1210285612Sdelphij			/* empty body */ ;
1211285612Sdelphij		}
121254359Sroberto
1213285612Sdelphij		if (f < numfrags && offset == offsets[f]) {
1214285612Sdelphij			TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
1215285612Sdelphij				  count, offset, counts[f], offsets[f]));
1216285612Sdelphij			continue;
1217285612Sdelphij		}
121854359Sroberto
1219285612Sdelphij		if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
1220285612Sdelphij			TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
1221285612Sdelphij				  offset, counts[f-1], offsets[f-1]));
1222285612Sdelphij			continue;
122354359Sroberto		}
1224285612Sdelphij
1225285612Sdelphij		if (f < numfrags && (offset + count) > offsets[f]) {
1226285612Sdelphij			TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
1227285612Sdelphij				  count, offset, offsets[f]));
1228285612Sdelphij			continue;
122954359Sroberto		}
123054359Sroberto
1231285612Sdelphij		for (ff = numfrags; ff > f; ff--) {
1232285612Sdelphij			offsets[ff] = offsets[ff-1];
1233285612Sdelphij			counts[ff] = counts[ff-1];
1234285612Sdelphij		}
1235285612Sdelphij		offsets[f] = offset;
1236285612Sdelphij		counts[f] = count;
1237285612Sdelphij		numfrags++;
123854359Sroberto
1239285612Sdelphij		/*
1240285612Sdelphij		 * Got that stuffed in right.  Figure out if this was the last.
1241285612Sdelphij		 * Record status info out of the last packet.
1242285612Sdelphij		 */
1243285612Sdelphij		if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1244285612Sdelphij			seenlastfrag = 1;
1245285612Sdelphij			if (rstatus != 0)
1246285612Sdelphij				*rstatus = ntohs(rpkt.status);
1247285612Sdelphij		}
124854359Sroberto
1249285612Sdelphij		/*
1250294569Sdelphij		 * Copy the data into the data buffer, and bump the
1251294569Sdelphij		 * timout base in case we need more.
1252285612Sdelphij		 */
1253285612Sdelphij		memcpy((char *)pktdata + offset, &rpkt.u, count);
1254294569Sdelphij		tobase = (uint32_t)time(NULL);
1255294569Sdelphij
1256285612Sdelphij		/*
1257285612Sdelphij		 * If we've seen the last fragment, look for holes in the sequence.
1258285612Sdelphij		 * If there aren't any, we're done.
1259285612Sdelphij		 */
1260301256Sdelphij#if !defined(SYS_WINNT) && defined(EINTR)
1261301256Sdelphij		maybe_final:
1262301256Sdelphij#endif
1263301256Sdelphij
1264285612Sdelphij		if (seenlastfrag && offsets[0] == 0) {
1265285612Sdelphij			for (f = 1; f < numfrags; f++)
1266285612Sdelphij				if (offsets[f-1] + counts[f-1] !=
1267285612Sdelphij				    offsets[f])
1268285612Sdelphij					break;
1269285612Sdelphij			if (f == numfrags) {
1270285612Sdelphij				*rsize = offsets[f-1] + counts[f-1];
1271285612Sdelphij				TRACE(1, ("%lu packets reassembled into response\n",
1272285612Sdelphij					  (u_long)numfrags));
1273285612Sdelphij				return 0;
1274285612Sdelphij			}
1275285612Sdelphij		}
1276285612Sdelphij	}  /* giant for (;;) collecting response packets */
1277285612Sdelphij}  /* getresponse() */
1278285612Sdelphij
1279285612Sdelphij
128054359Sroberto/*
128154359Sroberto * sendrequest - format and send a request packet
128254359Sroberto */
128354359Srobertostatic int
128454359Srobertosendrequest(
128554359Sroberto	int opcode,
1286285612Sdelphij	associd_t associd,
128754359Sroberto	int auth,
1288293650Sglebius	size_t qsize,
1289285612Sdelphij	const char *qdata
129054359Sroberto	)
129154359Sroberto{
129254359Sroberto	struct ntp_control qpkt;
1293293650Sglebius	size_t	pktsize;
1294285612Sdelphij	u_long	key_id;
1295285612Sdelphij	char *	pass;
1296293650Sglebius	size_t	maclen;
129754359Sroberto
129854359Sroberto	/*
129954359Sroberto	 * Check to make sure the data will fit in one packet
130054359Sroberto	 */
130154359Sroberto	if (qsize > CTL_MAX_DATA_LEN) {
1302285612Sdelphij		fprintf(stderr,
1303293650Sglebius			"***Internal error!  qsize (%zu) too large\n",
1304285612Sdelphij			qsize);
130554359Sroberto		return 1;
130654359Sroberto	}
130754359Sroberto
130854359Sroberto	/*
130954359Sroberto	 * Fill in the packet
131054359Sroberto	 */
131154359Sroberto	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1312132451Sroberto	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
131354359Sroberto	qpkt.sequence = htons(sequence);
131454359Sroberto	qpkt.status = 0;
131554359Sroberto	qpkt.associd = htons((u_short)associd);
131654359Sroberto	qpkt.offset = 0;
131754359Sroberto	qpkt.count = htons((u_short)qsize);
131854359Sroberto
1319285612Sdelphij	pktsize = CTL_HEADER_LEN;
1320285612Sdelphij
132154359Sroberto	/*
1322285612Sdelphij	 * If we have data, copy and pad it out to a 32-bit boundary.
132354359Sroberto	 */
132454359Sroberto	if (qsize > 0) {
1325285612Sdelphij		memcpy(&qpkt.u, qdata, (size_t)qsize);
1326285612Sdelphij		pktsize += qsize;
1327285612Sdelphij		while (pktsize & (sizeof(u_int32) - 1)) {
1328285612Sdelphij			qpkt.u.data[qsize++] = 0;
132954359Sroberto			pktsize++;
133054359Sroberto		}
133154359Sroberto	}
133254359Sroberto
133354359Sroberto	/*
133454359Sroberto	 * If it isn't authenticated we can just send it.  Otherwise
133554359Sroberto	 * we're going to have to think about it a little.
133654359Sroberto	 */
133754359Sroberto	if (!auth && !always_auth) {
1338285612Sdelphij		return sendpkt(&qpkt, pktsize);
1339285612Sdelphij	}
134054359Sroberto
1341285612Sdelphij	/*
1342285612Sdelphij	 * Pad out packet to a multiple of 8 octets to be sure
1343285612Sdelphij	 * receiver can handle it.
1344285612Sdelphij	 */
1345285612Sdelphij	while (pktsize & 7) {
1346285612Sdelphij		qpkt.u.data[qsize++] = 0;
1347285612Sdelphij		pktsize++;
1348285612Sdelphij	}
134954359Sroberto
1350285612Sdelphij	/*
1351285612Sdelphij	 * Get the keyid and the password if we don't have one.
1352285612Sdelphij	 */
1353285612Sdelphij	if (info_auth_keyid == 0) {
1354285612Sdelphij		key_id = getkeyid("Keyid: ");
1355285612Sdelphij		if (key_id == 0 || key_id > NTP_MAXKEY) {
1356285612Sdelphij			fprintf(stderr,
1357285612Sdelphij				"Invalid key identifier\n");
1358285612Sdelphij			return 1;
135954359Sroberto		}
1360285612Sdelphij		info_auth_keyid = key_id;
1361285612Sdelphij	}
1362285612Sdelphij	if (!authistrusted(info_auth_keyid)) {
1363285612Sdelphij		pass = getpass_keytype(info_auth_keytype);
1364285612Sdelphij		if ('\0' == pass[0]) {
1365285612Sdelphij			fprintf(stderr, "Invalid password\n");
1366285612Sdelphij			return 1;
136754359Sroberto		}
1368285612Sdelphij		authusekey(info_auth_keyid, info_auth_keytype,
1369285612Sdelphij			   (u_char *)pass);
137054359Sroberto		authtrust(info_auth_keyid, 1);
1371285612Sdelphij	}
137254359Sroberto
1373285612Sdelphij	/*
1374285612Sdelphij	 * Do the encryption.
1375285612Sdelphij	 */
1376285612Sdelphij	maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1377285612Sdelphij	if (!maclen) {
1378285612Sdelphij		fprintf(stderr, "Key not found\n");
1379285612Sdelphij		return 1;
1380285612Sdelphij	} else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1381285612Sdelphij		fprintf(stderr,
1382293650Sglebius			"%zu octet MAC, %zu expected with %zu octet digest\n",
1383285612Sdelphij			maclen, (info_auth_hashlen + sizeof(keyid_t)),
1384285612Sdelphij			info_auth_hashlen);
1385285612Sdelphij		return 1;
138654359Sroberto	}
1387285612Sdelphij
1388285612Sdelphij	return sendpkt((char *)&qpkt, pktsize + maclen);
138954359Sroberto}
139054359Sroberto
139154359Sroberto
139254359Sroberto/*
1393285612Sdelphij * show_error_msg - display the error text for a mode 6 error response.
139454359Sroberto */
1395285612Sdelphijvoid
1396285612Sdelphijshow_error_msg(
1397285612Sdelphij	int		m6resp,
1398285612Sdelphij	associd_t	associd
1399285612Sdelphij	)
1400285612Sdelphij{
1401285612Sdelphij	if (numhosts > 1)
1402285612Sdelphij		fprintf(stderr, "server=%s ", currenthost);
1403285612Sdelphij
1404298699Sdelphij	switch (m6resp) {
1405285612Sdelphij
1406285612Sdelphij	case CERR_BADFMT:
1407285612Sdelphij		fprintf(stderr,
1408285612Sdelphij		    "***Server reports a bad format request packet\n");
1409285612Sdelphij		break;
1410285612Sdelphij
1411285612Sdelphij	case CERR_PERMISSION:
1412285612Sdelphij		fprintf(stderr,
1413285612Sdelphij		    "***Server disallowed request (authentication?)\n");
1414285612Sdelphij		break;
1415285612Sdelphij
1416285612Sdelphij	case CERR_BADOP:
1417285612Sdelphij		fprintf(stderr,
1418285612Sdelphij		    "***Server reports a bad opcode in request\n");
1419285612Sdelphij		break;
1420285612Sdelphij
1421285612Sdelphij	case CERR_BADASSOC:
1422285612Sdelphij		fprintf(stderr,
1423285612Sdelphij		    "***Association ID %d unknown to server\n",
1424285612Sdelphij		    associd);
1425285612Sdelphij		break;
1426285612Sdelphij
1427285612Sdelphij	case CERR_UNKNOWNVAR:
1428285612Sdelphij		fprintf(stderr,
1429285612Sdelphij		    "***A request variable unknown to the server\n");
1430285612Sdelphij		break;
1431285612Sdelphij
1432285612Sdelphij	case CERR_BADVALUE:
1433285612Sdelphij		fprintf(stderr,
1434285612Sdelphij		    "***Server indicates a request variable was bad\n");
1435285612Sdelphij		break;
1436285612Sdelphij
1437285612Sdelphij	case ERR_UNSPEC:
1438285612Sdelphij		fprintf(stderr,
1439285612Sdelphij		    "***Server returned an unspecified error\n");
1440285612Sdelphij		break;
1441285612Sdelphij
1442285612Sdelphij	case ERR_TIMEOUT:
1443285612Sdelphij		fprintf(stderr, "***Request timed out\n");
1444285612Sdelphij		break;
1445285612Sdelphij
1446285612Sdelphij	case ERR_INCOMPLETE:
1447285612Sdelphij		fprintf(stderr,
1448285612Sdelphij		    "***Response from server was incomplete\n");
1449285612Sdelphij		break;
1450285612Sdelphij
1451285612Sdelphij	case ERR_TOOMUCH:
1452285612Sdelphij		fprintf(stderr,
1453285612Sdelphij		    "***Buffer size exceeded for returned data\n");
1454285612Sdelphij		break;
1455285612Sdelphij
1456285612Sdelphij	default:
1457285612Sdelphij		fprintf(stderr,
1458285612Sdelphij		    "***Server returns unknown error code %d\n",
1459285612Sdelphij		    m6resp);
1460285612Sdelphij	}
1461285612Sdelphij}
1462285612Sdelphij
1463285612Sdelphij/*
1464285612Sdelphij * doquery - send a request and process the response, displaying
1465285612Sdelphij *	     error messages for any error responses.
1466285612Sdelphij */
146754359Srobertoint
146854359Srobertodoquery(
146954359Sroberto	int opcode,
1470285612Sdelphij	associd_t associd,
147154359Sroberto	int auth,
1472293650Sglebius	size_t qsize,
1473285612Sdelphij	const char *qdata,
147454359Sroberto	u_short *rstatus,
1475293650Sglebius	size_t *rsize,
1476285612Sdelphij	const char **rdata
147754359Sroberto	)
147854359Sroberto{
1479285612Sdelphij	return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
1480285612Sdelphij			 rsize, rdata, FALSE);
1481285612Sdelphij}
1482285612Sdelphij
1483285612Sdelphij
1484285612Sdelphij/*
1485285612Sdelphij * doqueryex - send a request and process the response, optionally
1486285612Sdelphij *	       displaying error messages for any error responses.
1487285612Sdelphij */
1488285612Sdelphijint
1489285612Sdelphijdoqueryex(
1490285612Sdelphij	int opcode,
1491285612Sdelphij	associd_t associd,
1492285612Sdelphij	int auth,
1493293650Sglebius	size_t qsize,
1494285612Sdelphij	const char *qdata,
1495285612Sdelphij	u_short *rstatus,
1496293650Sglebius	size_t *rsize,
1497285612Sdelphij	const char **rdata,
1498285612Sdelphij	int quiet
1499285612Sdelphij	)
1500285612Sdelphij{
150154359Sroberto	int res;
150254359Sroberto	int done;
150354359Sroberto
150454359Sroberto	/*
150554359Sroberto	 * Check to make sure host is open
150654359Sroberto	 */
150754359Sroberto	if (!havehost) {
1508285612Sdelphij		fprintf(stderr, "***No host open, use `host' command\n");
150954359Sroberto		return -1;
151054359Sroberto	}
151154359Sroberto
151254359Sroberto	done = 0;
151354359Sroberto	sequence++;
151454359Sroberto
151554359Sroberto    again:
151654359Sroberto	/*
151754359Sroberto	 * send a request
151854359Sroberto	 */
151954359Sroberto	res = sendrequest(opcode, associd, auth, qsize, qdata);
152054359Sroberto	if (res != 0)
1521285612Sdelphij		return res;
1522285612Sdelphij
152354359Sroberto	/*
152454359Sroberto	 * Get the response.  If we got a standard error, print a message
152554359Sroberto	 */
152654359Sroberto	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
152754359Sroberto
152854359Sroberto	if (res > 0) {
152954359Sroberto		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
153054359Sroberto			if (res == ERR_INCOMPLETE) {
153154359Sroberto				/*
153254359Sroberto				 * better bump the sequence so we don't
153354359Sroberto				 * get confused about differing fragments.
153454359Sroberto				 */
153554359Sroberto				sequence++;
153654359Sroberto			}
153754359Sroberto			done = 1;
153854359Sroberto			goto again;
153954359Sroberto		}
1540285612Sdelphij		if (!quiet)
1541285612Sdelphij			show_error_msg(res, associd);
1542285612Sdelphij
154354359Sroberto	}
154454359Sroberto	return res;
154554359Sroberto}
154654359Sroberto
154754359Sroberto
1548285612Sdelphij#ifndef BUILD_AS_LIB
154954359Sroberto/*
155054359Sroberto * getcmds - read commands from the standard input and execute them
155154359Sroberto */
155254359Srobertostatic void
155354359Srobertogetcmds(void)
155454359Sroberto{
1555285612Sdelphij	char *	line;
1556285612Sdelphij	int	count;
155754359Sroberto
1558285612Sdelphij	ntp_readline_init(interactive ? prompt : NULL);
1559106163Sroberto
1560285612Sdelphij	for (;;) {
1561285612Sdelphij		line = ntp_readline(&count);
1562285612Sdelphij		if (NULL == line)
1563285612Sdelphij			break;
1564285612Sdelphij		docmd(line);
1565285612Sdelphij		free(line);
1566285612Sdelphij	}
156754359Sroberto
1568285612Sdelphij	ntp_readline_uninit();
156954359Sroberto}
1570285612Sdelphij#endif /* !BUILD_AS_LIB */
157154359Sroberto
1572285612Sdelphij
1573285612Sdelphij#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
157454359Sroberto/*
157554359Sroberto * abortcmd - catch interrupts and abort the current command
157654359Sroberto */
1577293650Sglebiusstatic int
1578293650Sglebiusabortcmd(void)
157954359Sroberto{
158054359Sroberto	if (current_output == stdout)
1581293650Sglebius		(void) fflush(stdout);
158254359Sroberto	putc('\n', stderr);
158354359Sroberto	(void) fflush(stderr);
1584293650Sglebius	if (jump) {
1585293650Sglebius		jump = 0;
1586338531Sdelphij		LONGJMP(interrupt_buf, 1);
1587293650Sglebius	}
1588293650Sglebius	return TRUE;
158954359Sroberto}
1590285612Sdelphij#endif	/* !SYS_WINNT && !BUILD_AS_LIB */
159154359Sroberto
1592285612Sdelphij
1593285612Sdelphij#ifndef	BUILD_AS_LIB
159454359Sroberto/*
159554359Sroberto * docmd - decode the command line and execute a command
159654359Sroberto */
159754359Srobertostatic void
159854359Srobertodocmd(
159954359Sroberto	const char *cmdline
160054359Sroberto	)
160154359Sroberto{
160254359Sroberto	char *tokens[1+MAXARGS+2];
160354359Sroberto	struct parse pcmd;
160454359Sroberto	int ntok;
160554359Sroberto	static int i;
160654359Sroberto	struct xcmd *xcmd;
160754359Sroberto
160854359Sroberto	/*
160954359Sroberto	 * Tokenize the command line.  If nothing on it, return.
161054359Sroberto	 */
161154359Sroberto	tokenize(cmdline, tokens, &ntok);
161254359Sroberto	if (ntok == 0)
161354359Sroberto	    return;
1614285612Sdelphij
161554359Sroberto	/*
161654359Sroberto	 * Find the appropriate command description.
161754359Sroberto	 */
161854359Sroberto	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
161954359Sroberto	if (i == 0) {
162054359Sroberto		(void) fprintf(stderr, "***Command `%s' unknown\n",
162154359Sroberto			       tokens[0]);
162254359Sroberto		return;
162354359Sroberto	} else if (i >= 2) {
162454359Sroberto		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
162554359Sroberto			       tokens[0]);
162654359Sroberto		return;
162754359Sroberto	}
1628285612Sdelphij
1629285612Sdelphij	/* Warn about ignored extra args */
1630285612Sdelphij	for (i = MAXARGS + 1; i < ntok ; ++i) {
1631285612Sdelphij		fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
1632285612Sdelphij	}
1633285612Sdelphij
163454359Sroberto	/*
163554359Sroberto	 * Save the keyword, then walk through the arguments, interpreting
163654359Sroberto	 * as we go.
163754359Sroberto	 */
163854359Sroberto	pcmd.keyword = tokens[0];
163954359Sroberto	pcmd.nargs = 0;
164054359Sroberto	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
164154359Sroberto		if ((i+1) >= ntok) {
164254359Sroberto			if (!(xcmd->arg[i] & OPT)) {
164354359Sroberto				printusage(xcmd, stderr);
164454359Sroberto				return;
164554359Sroberto			}
164654359Sroberto			break;
164754359Sroberto		}
164854359Sroberto		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1649285612Sdelphij			break;
165054359Sroberto		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1651285612Sdelphij			return;
165254359Sroberto		pcmd.nargs++;
165354359Sroberto	}
165454359Sroberto
165554359Sroberto	i++;
165654359Sroberto	if (i < ntok && *tokens[i] == '>') {
165754359Sroberto		char *fname;
165854359Sroberto
165954359Sroberto		if (*(tokens[i]+1) != '\0')
1660285612Sdelphij			fname = tokens[i]+1;
166154359Sroberto		else if ((i+1) < ntok)
1662285612Sdelphij			fname = tokens[i+1];
166354359Sroberto		else {
166454359Sroberto			(void) fprintf(stderr, "***No file for redirect\n");
166554359Sroberto			return;
166654359Sroberto		}
166754359Sroberto
166854359Sroberto		current_output = fopen(fname, "w");
166954359Sroberto		if (current_output == NULL) {
167054359Sroberto			(void) fprintf(stderr, "***Error opening %s: ", fname);
167154359Sroberto			perror("");
167254359Sroberto			return;
167354359Sroberto		}
167454359Sroberto	} else {
167554359Sroberto		current_output = stdout;
167654359Sroberto	}
167754359Sroberto
1678338531Sdelphij	if (interactive) {
1679338531Sdelphij		if ( ! SETJMP(interrupt_buf)) {
1680338531Sdelphij			jump = 1;
1681338531Sdelphij			(xcmd->handler)(&pcmd, current_output);
1682338531Sdelphij			jump = 0;
1683338531Sdelphij		} else {
1684338531Sdelphij			fflush(current_output);
1685338531Sdelphij			fputs("\n >>> command aborted <<<\n", stderr);
1686338531Sdelphij			fflush(stderr);
1687338531Sdelphij		}
1688338531Sdelphij
1689338531Sdelphij	} else {
169054359Sroberto		jump = 0;
169154359Sroberto		(xcmd->handler)(&pcmd, current_output);
169254359Sroberto	}
1693338531Sdelphij	if ((NULL != current_output) && (stdout != current_output)) {
1694338531Sdelphij		(void)fclose(current_output);
1695338531Sdelphij		current_output = NULL;
1696338531Sdelphij	}
169754359Sroberto}
169854359Sroberto
169954359Sroberto
170054359Sroberto/*
170154359Sroberto * tokenize - turn a command line into tokens
1702285612Sdelphij *
1703285612Sdelphij * SK: Modified to allow a quoted string
1704285612Sdelphij *
1705285612Sdelphij * HMS: If the first character of the first token is a ':' then (after
1706285612Sdelphij * eating inter-token whitespace) the 2nd token is the rest of the line.
170754359Sroberto */
1708285612Sdelphij
170954359Srobertostatic void
171054359Srobertotokenize(
171154359Sroberto	const char *line,
171254359Sroberto	char **tokens,
171354359Sroberto	int *ntok
171454359Sroberto	)
171554359Sroberto{
171654359Sroberto	register const char *cp;
171754359Sroberto	register char *sp;
171854359Sroberto	static char tspace[MAXLINE];
171954359Sroberto
172054359Sroberto	sp = tspace;
172154359Sroberto	cp = line;
172254359Sroberto	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
172354359Sroberto		tokens[*ntok] = sp;
1724285612Sdelphij
1725285612Sdelphij		/* Skip inter-token whitespace */
172654359Sroberto		while (ISSPACE(*cp))
172754359Sroberto		    cp++;
1728285612Sdelphij
1729285612Sdelphij		/* If we're at EOL we're done */
173054359Sroberto		if (ISEOL(*cp))
173154359Sroberto		    break;
173254359Sroberto
1733285612Sdelphij		/* If this is the 2nd token and the first token begins
1734285612Sdelphij		 * with a ':', then just grab to EOL.
1735285612Sdelphij		 */
1736285612Sdelphij
1737285612Sdelphij		if (*ntok == 1 && tokens[0][0] == ':') {
1738285612Sdelphij			do {
1739285612Sdelphij				if (sp - tspace >= MAXLINE)
1740285612Sdelphij					goto toobig;
1741285612Sdelphij				*sp++ = *cp++;
1742285612Sdelphij			} while (!ISEOL(*cp));
1743285612Sdelphij		}
1744285612Sdelphij
1745285612Sdelphij		/* Check if this token begins with a double quote.
1746285612Sdelphij		 * If yes, continue reading till the next double quote
1747285612Sdelphij		 */
1748285612Sdelphij		else if (*cp == '\"') {
1749285612Sdelphij			++cp;
1750285612Sdelphij			do {
1751285612Sdelphij				if (sp - tspace >= MAXLINE)
1752285612Sdelphij					goto toobig;
1753285612Sdelphij				*sp++ = *cp++;
1754285612Sdelphij			} while ((*cp != '\"') && !ISEOL(*cp));
1755285612Sdelphij			/* HMS: a missing closing " should be an error */
1756285612Sdelphij		}
1757285612Sdelphij		else {
1758285612Sdelphij			do {
1759285612Sdelphij				if (sp - tspace >= MAXLINE)
1760285612Sdelphij					goto toobig;
1761285612Sdelphij				*sp++ = *cp++;
1762285612Sdelphij			} while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1763285612Sdelphij			/* HMS: Why check for a " in the previous line? */
1764285612Sdelphij		}
1765285612Sdelphij
1766285612Sdelphij		if (sp - tspace >= MAXLINE)
1767285612Sdelphij			goto toobig;
176854359Sroberto		*sp++ = '\0';
176954359Sroberto	}
1770285612Sdelphij	return;
1771285612Sdelphij
1772285612Sdelphij  toobig:
1773285612Sdelphij	*ntok = 0;
1774285612Sdelphij	fprintf(stderr,
1775285612Sdelphij		"***Line `%s' is too big\n",
1776285612Sdelphij		line);
1777285612Sdelphij	return;
177854359Sroberto}
177954359Sroberto
178054359Sroberto
1781285612Sdelphij/*
1782285612Sdelphij * getarg - interpret an argument token
1783285612Sdelphij */
1784285612Sdelphijstatic int
1785285612Sdelphijgetarg(
1786285612Sdelphij	const char *str,
1787285612Sdelphij	int code,
1788285612Sdelphij	arg_v *argp
1789285612Sdelphij	)
1790285612Sdelphij{
1791285612Sdelphij	u_long ul;
179254359Sroberto
1793285612Sdelphij	switch (code & ~OPT) {
1794285612Sdelphij	case NTP_STR:
1795285612Sdelphij		argp->string = str;
1796285612Sdelphij		break;
1797285612Sdelphij
1798285612Sdelphij	case NTP_ADD:
1799285612Sdelphij		if (!getnetnum(str, &argp->netnum, NULL, 0))
1800285612Sdelphij			return 0;
1801285612Sdelphij		break;
1802285612Sdelphij
1803285612Sdelphij	case NTP_UINT:
1804285612Sdelphij		if ('&' == str[0]) {
1805285612Sdelphij			if (!atouint(&str[1], &ul)) {
1806285612Sdelphij				fprintf(stderr,
1807285612Sdelphij					"***Association index `%s' invalid/undecodable\n",
1808285612Sdelphij					str);
1809285612Sdelphij				return 0;
1810285612Sdelphij			}
1811285612Sdelphij			if (0 == numassoc) {
1812285612Sdelphij				dogetassoc(stdout);
1813285612Sdelphij				if (0 == numassoc) {
1814285612Sdelphij					fprintf(stderr,
1815285612Sdelphij						"***No associations found, `%s' unknown\n",
1816285612Sdelphij						str);
1817285612Sdelphij					return 0;
1818285612Sdelphij				}
1819285612Sdelphij			}
1820285612Sdelphij			ul = min(ul, numassoc);
1821285612Sdelphij			argp->uval = assoc_cache[ul - 1].assid;
1822285612Sdelphij			break;
1823285612Sdelphij		}
1824285612Sdelphij		if (!atouint(str, &argp->uval)) {
1825285612Sdelphij			fprintf(stderr, "***Illegal unsigned value %s\n",
1826285612Sdelphij				str);
1827285612Sdelphij			return 0;
1828285612Sdelphij		}
1829285612Sdelphij		break;
1830285612Sdelphij
1831285612Sdelphij	case NTP_INT:
1832285612Sdelphij		if (!atoint(str, &argp->ival)) {
1833285612Sdelphij			fprintf(stderr, "***Illegal integer value %s\n",
1834285612Sdelphij				str);
1835285612Sdelphij			return 0;
1836285612Sdelphij		}
1837285612Sdelphij		break;
1838285612Sdelphij
1839285612Sdelphij	case IP_VERSION:
1840285612Sdelphij		if (!strcmp("-6", str)) {
1841285612Sdelphij			argp->ival = 6;
1842285612Sdelphij		} else if (!strcmp("-4", str)) {
1843285612Sdelphij			argp->ival = 4;
1844285612Sdelphij		} else {
1845285612Sdelphij			fprintf(stderr, "***Version must be either 4 or 6\n");
1846285612Sdelphij			return 0;
1847285612Sdelphij		}
1848285612Sdelphij		break;
1849285612Sdelphij	}
1850285612Sdelphij
1851285612Sdelphij	return 1;
1852285612Sdelphij}
1853285612Sdelphij#endif	/* !BUILD_AS_LIB */
1854285612Sdelphij
1855285612Sdelphij
185654359Sroberto/*
185754359Sroberto * findcmd - find a command in a command description table
185854359Sroberto */
185954359Srobertostatic int
186054359Srobertofindcmd(
1861285612Sdelphij	const char *	str,
1862285612Sdelphij	struct xcmd *	clist1,
1863285612Sdelphij	struct xcmd *	clist2,
1864285612Sdelphij	struct xcmd **	cmd
186554359Sroberto	)
186654359Sroberto{
1867285612Sdelphij	struct xcmd *cl;
1868293650Sglebius	size_t clen;
186954359Sroberto	int nmatch;
187054359Sroberto	struct xcmd *nearmatch = NULL;
187154359Sroberto	struct xcmd *clist;
187254359Sroberto
187354359Sroberto	clen = strlen(str);
187454359Sroberto	nmatch = 0;
187554359Sroberto	if (clist1 != 0)
187654359Sroberto	    clist = clist1;
187754359Sroberto	else if (clist2 != 0)
187854359Sroberto	    clist = clist2;
187954359Sroberto	else
188054359Sroberto	    return 0;
188154359Sroberto
188254359Sroberto    again:
188354359Sroberto	for (cl = clist; cl->keyword != 0; cl++) {
188454359Sroberto		/* do a first character check, for efficiency */
188554359Sroberto		if (*str != *(cl->keyword))
188654359Sroberto		    continue;
188754359Sroberto		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
188854359Sroberto			/*
188954359Sroberto			 * Could be extact match, could be approximate.
189054359Sroberto			 * Is exact if the length of the keyword is the
189154359Sroberto			 * same as the str.
189254359Sroberto			 */
189354359Sroberto			if (*((cl->keyword) + clen) == '\0') {
189454359Sroberto				*cmd = cl;
189554359Sroberto				return 1;
189654359Sroberto			}
189754359Sroberto			nmatch++;
189854359Sroberto			nearmatch = cl;
189954359Sroberto		}
190054359Sroberto	}
190154359Sroberto
190254359Sroberto	/*
190354359Sroberto	 * See if there is more to do.  If so, go again.  Sorry about the
190454359Sroberto	 * goto, too much looking at BSD sources...
190554359Sroberto	 */
190654359Sroberto	if (clist == clist1 && clist2 != 0) {
190754359Sroberto		clist = clist2;
190854359Sroberto		goto again;
190954359Sroberto	}
191054359Sroberto
191154359Sroberto	/*
191254359Sroberto	 * If we got extactly 1 near match, use it, else return number
191354359Sroberto	 * of matches.
191454359Sroberto	 */
191554359Sroberto	if (nmatch == 1) {
191654359Sroberto		*cmd = nearmatch;
191754359Sroberto		return 1;
191854359Sroberto	}
191954359Sroberto	return nmatch;
192054359Sroberto}
192154359Sroberto
192254359Sroberto
192354359Sroberto/*
192454359Sroberto * getnetnum - given a host name, return its net number
192554359Sroberto *	       and (optional) full name
192654359Sroberto */
192754359Srobertoint
192854359Srobertogetnetnum(
192954359Sroberto	const char *hname,
1930285612Sdelphij	sockaddr_u *num,
1931132451Sroberto	char *fullhost,
1932132451Sroberto	int af
193354359Sroberto	)
193454359Sroberto{
1935132451Sroberto	struct addrinfo hints, *ai = NULL;
193654359Sroberto
1937285612Sdelphij	ZERO(hints);
1938132451Sroberto	hints.ai_flags = AI_CANONNAME;
1939132451Sroberto#ifdef AI_ADDRCONFIG
1940132451Sroberto	hints.ai_flags |= AI_ADDRCONFIG;
1941132451Sroberto#endif
1942285612Sdelphij
1943285612Sdelphij	/*
1944285612Sdelphij	 * decodenetnum only works with addresses, but handles syntax
1945285612Sdelphij	 * that getaddrinfo doesn't:  [2001::1]:1234
1946285612Sdelphij	 */
194754359Sroberto	if (decodenetnum(hname, num)) {
1948285612Sdelphij		if (fullhost != NULL)
1949285612Sdelphij			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1950285612Sdelphij				    LENHOSTNAME, NULL, 0, 0);
195154359Sroberto		return 1;
1952182007Sroberto	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1953285612Sdelphij		INSIST(sizeof(*num) >= ai->ai_addrlen);
1954285612Sdelphij		memcpy(num, ai->ai_addr, ai->ai_addrlen);
1955285612Sdelphij		if (fullhost != NULL) {
1956285612Sdelphij			if (ai->ai_canonname != NULL)
1957285612Sdelphij				strlcpy(fullhost, ai->ai_canonname,
1958285612Sdelphij					LENHOSTNAME);
1959285612Sdelphij			else
1960285612Sdelphij				getnameinfo(&num->sa, SOCKLEN(num),
1961285612Sdelphij					    fullhost, LENHOSTNAME, NULL,
1962285612Sdelphij					    0, 0);
1963285612Sdelphij		}
1964285612Sdelphij		freeaddrinfo(ai);
196554359Sroberto		return 1;
196654359Sroberto	}
1967285612Sdelphij	fprintf(stderr, "***Can't find host %s\n", hname);
1968285612Sdelphij
1969285612Sdelphij	return 0;
197054359Sroberto}
197154359Sroberto
1972285612Sdelphij
197354359Sroberto/*
197454359Sroberto * nntohost - convert network number to host name.  This routine enforces
197554359Sroberto *	       the showhostnames setting.
197654359Sroberto */
1977285612Sdelphijconst char *
197854359Srobertonntohost(
1979285612Sdelphij	sockaddr_u *netnum
198054359Sroberto	)
198154359Sroberto{
1982285612Sdelphij	return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
198354359Sroberto}
198454359Sroberto
198554359Sroberto
198654359Sroberto/*
1987285612Sdelphij * nntohost_col - convert network number to host name in fixed width.
1988285612Sdelphij *		  This routine enforces the showhostnames setting.
1989285612Sdelphij *		  When displaying hostnames longer than the width,
1990285612Sdelphij *		  the first part of the hostname is displayed.  When
1991285612Sdelphij *		  displaying numeric addresses longer than the width,
1992285612Sdelphij *		  Such as IPv6 addresses, the caller decides whether
1993285612Sdelphij *		  the first or last of the numeric address is used.
1994285612Sdelphij */
1995285612Sdelphijconst char *
1996285612Sdelphijnntohost_col(
1997285612Sdelphij	sockaddr_u *	addr,
1998285612Sdelphij	size_t		width,
1999285612Sdelphij	int		preserve_lowaddrbits
2000285612Sdelphij	)
2001285612Sdelphij{
2002285612Sdelphij	const char *	out;
2003285612Sdelphij
2004285612Sdelphij	if (!showhostnames || SOCK_UNSPEC(addr)) {
2005285612Sdelphij		if (preserve_lowaddrbits)
2006285612Sdelphij			out = trunc_left(stoa(addr), width);
2007285612Sdelphij		else
2008285612Sdelphij			out = trunc_right(stoa(addr), width);
2009285612Sdelphij	} else if (ISREFCLOCKADR(addr)) {
2010285612Sdelphij		out = refnumtoa(addr);
2011285612Sdelphij	} else {
2012285612Sdelphij		out = trunc_right(socktohost(addr), width);
2013285612Sdelphij	}
2014285612Sdelphij	return out;
2015285612Sdelphij}
2016285612Sdelphij
2017285612Sdelphij
2018285612Sdelphij/*
2019285612Sdelphij * nntohostp() is the same as nntohost() plus a :port suffix
2020285612Sdelphij */
2021285612Sdelphijconst char *
2022285612Sdelphijnntohostp(
2023285612Sdelphij	sockaddr_u *netnum
2024285612Sdelphij	)
2025285612Sdelphij{
2026285612Sdelphij	const char *	hostn;
2027285612Sdelphij	char *		buf;
2028285612Sdelphij
2029285612Sdelphij	if (!showhostnames || SOCK_UNSPEC(netnum))
2030285612Sdelphij		return sptoa(netnum);
2031285612Sdelphij	else if (ISREFCLOCKADR(netnum))
2032285612Sdelphij		return refnumtoa(netnum);
2033285612Sdelphij
2034285612Sdelphij	hostn = socktohost(netnum);
2035285612Sdelphij	LIB_GETBUF(buf);
2036285612Sdelphij	snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
2037285612Sdelphij
2038285612Sdelphij	return buf;
2039285612Sdelphij}
2040285612Sdelphij
2041285612Sdelphij/*
204254359Sroberto * rtdatetolfp - decode an RT-11 date into an l_fp
204354359Sroberto */
204454359Srobertostatic int
204554359Srobertortdatetolfp(
204654359Sroberto	char *str,
204754359Sroberto	l_fp *lfp
204854359Sroberto	)
204954359Sroberto{
205054359Sroberto	register char *cp;
205154359Sroberto	register int i;
205254359Sroberto	struct calendar cal;
205354359Sroberto	char buf[4];
205454359Sroberto
205554359Sroberto	cal.yearday = 0;
205654359Sroberto
205754359Sroberto	/*
205854359Sroberto	 * An RT-11 date looks like:
205954359Sroberto	 *
206054359Sroberto	 * d[d]-Mth-y[y] hh:mm:ss
206154359Sroberto	 *
206254359Sroberto	 * (No docs, but assume 4-digit years are also legal...)
206354359Sroberto	 *
206454359Sroberto	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
206554359Sroberto	 */
206654359Sroberto	cp = str;
2067330141Sdelphij	if (!isdigit(pgetc(cp))) {
206854359Sroberto		if (*cp == '-') {
206954359Sroberto			/*
207054359Sroberto			 * Catch special case
207154359Sroberto			 */
207254359Sroberto			L_CLR(lfp);
207354359Sroberto			return 1;
207454359Sroberto		}
207554359Sroberto		return 0;
207654359Sroberto	}
207754359Sroberto
2078132451Sroberto	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
2079330141Sdelphij	if (isdigit(pgetc(cp))) {
2080132451Sroberto		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
2081132451Sroberto		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
208254359Sroberto	}
208354359Sroberto
208454359Sroberto	if (*cp++ != '-')
208554359Sroberto	    return 0;
2086285612Sdelphij
208754359Sroberto	for (i = 0; i < 3; i++)
208854359Sroberto	    buf[i] = *cp++;
208954359Sroberto	buf[3] = '\0';
209054359Sroberto
209154359Sroberto	for (i = 0; i < 12; i++)
209254359Sroberto	    if (STREQ(buf, months[i]))
209354359Sroberto		break;
209454359Sroberto	if (i == 12)
209554359Sroberto	    return 0;
2096132451Sroberto	cal.month = (u_char)(i + 1);
209754359Sroberto
209854359Sroberto	if (*cp++ != '-')
209954359Sroberto	    return 0;
2100285612Sdelphij
2101330141Sdelphij	if (!isdigit(pgetc(cp)))
210254359Sroberto	    return 0;
2103132451Sroberto	cal.year = (u_short)(*cp++ - '0');
2104330141Sdelphij	if (isdigit(pgetc(cp))) {
2105132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2106132451Sroberto		cal.year = (u_short)(*cp++ - '0');
210754359Sroberto	}
2108330141Sdelphij	if (isdigit(pgetc(cp))) {
2109132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2110132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
211154359Sroberto	}
2112330141Sdelphij	if (isdigit(pgetc(cp))) {
2113132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2114132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
211554359Sroberto	}
211654359Sroberto
211754359Sroberto	/*
211854359Sroberto	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
211954359Sroberto	 */
212054359Sroberto	if (cal.year == 0) {
212154359Sroberto		L_CLR(lfp);
212254359Sroberto		return 1;
212354359Sroberto	}
212454359Sroberto
2125330141Sdelphij	if (*cp++ != ' ' || !isdigit(pgetc(cp)))
212654359Sroberto	    return 0;
2127132451Sroberto	cal.hour = (u_char)(*cp++ - '0');
2128330141Sdelphij	if (isdigit(pgetc(cp))) {
2129132451Sroberto		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
2130132451Sroberto		cal.hour = (u_char)(cal.hour + *cp++ - '0');
213154359Sroberto	}
213254359Sroberto
2133330141Sdelphij	if (*cp++ != ':' || !isdigit(pgetc(cp)))
213454359Sroberto	    return 0;
2135132451Sroberto	cal.minute = (u_char)(*cp++ - '0');
2136330141Sdelphij	if (isdigit(pgetc(cp))) {
2137132451Sroberto		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
2138132451Sroberto		cal.minute = (u_char)(cal.minute + *cp++ - '0');
213954359Sroberto	}
214054359Sroberto
2141330141Sdelphij	if (*cp++ != ':' || !isdigit(pgetc(cp)))
214254359Sroberto	    return 0;
2143132451Sroberto	cal.second = (u_char)(*cp++ - '0');
2144330141Sdelphij	if (isdigit(pgetc(cp))) {
2145132451Sroberto		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
2146132451Sroberto		cal.second = (u_char)(cal.second + *cp++ - '0');
214754359Sroberto	}
214854359Sroberto
214954359Sroberto	/*
215054359Sroberto	 * For RT-11, 1972 seems to be the pivot year
215154359Sroberto	 */
215254359Sroberto	if (cal.year < 72)
215354359Sroberto		cal.year += 2000;
215454359Sroberto	if (cal.year < 100)
215554359Sroberto		cal.year += 1900;
215654359Sroberto
215754359Sroberto	lfp->l_ui = caltontp(&cal);
215854359Sroberto	lfp->l_uf = 0;
215954359Sroberto	return 1;
216054359Sroberto}
216154359Sroberto
216254359Sroberto
216354359Sroberto/*
216454359Sroberto * decodets - decode a timestamp into an l_fp format number, with
216554359Sroberto *	      consideration of fuzzball formats.
216654359Sroberto */
216754359Srobertoint
216854359Srobertodecodets(
216954359Sroberto	char *str,
217054359Sroberto	l_fp *lfp
217154359Sroberto	)
217254359Sroberto{
2173285612Sdelphij	char *cp;
2174285612Sdelphij	char buf[30];
2175285612Sdelphij	size_t b;
2176285612Sdelphij
217754359Sroberto	/*
217854359Sroberto	 * If it starts with a 0x, decode as hex.
217954359Sroberto	 */
218054359Sroberto	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2181285612Sdelphij		return hextolfp(str+2, lfp);
218254359Sroberto
218354359Sroberto	/*
218454359Sroberto	 * If it starts with a '"', try it as an RT-11 date.
218554359Sroberto	 */
218654359Sroberto	if (*str == '"') {
2187285612Sdelphij		cp = str + 1;
2188285612Sdelphij		b = 0;
2189285612Sdelphij		while ('"' != *cp && '\0' != *cp &&
2190285612Sdelphij		       b < COUNTOF(buf) - 1)
2191285612Sdelphij			buf[b++] = *cp++;
2192285612Sdelphij		buf[b] = '\0';
219354359Sroberto		return rtdatetolfp(buf, lfp);
219454359Sroberto	}
219554359Sroberto
219654359Sroberto	/*
219754359Sroberto	 * Might still be hex.  Check out the first character.  Talk
219854359Sroberto	 * about heuristics!
219954359Sroberto	 */
220054359Sroberto	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2201285612Sdelphij		return hextolfp(str, lfp);
220254359Sroberto
220354359Sroberto	/*
220454359Sroberto	 * Try it as a decimal.  If this fails, try as an unquoted
220554359Sroberto	 * RT-11 date.  This code should go away eventually.
220654359Sroberto	 */
220754359Sroberto	if (atolfp(str, lfp))
2208285612Sdelphij		return 1;
2209285612Sdelphij
221054359Sroberto	return rtdatetolfp(str, lfp);
221154359Sroberto}
221254359Sroberto
221354359Sroberto
221454359Sroberto/*
221554359Sroberto * decodetime - decode a time value.  It should be in milliseconds
221654359Sroberto */
221754359Srobertoint
221854359Srobertodecodetime(
221954359Sroberto	char *str,
222054359Sroberto	l_fp *lfp
222154359Sroberto	)
222254359Sroberto{
222354359Sroberto	return mstolfp(str, lfp);
222454359Sroberto}
222554359Sroberto
222654359Sroberto
222754359Sroberto/*
222854359Sroberto * decodeint - decode an integer
222954359Sroberto */
223054359Srobertoint
223154359Srobertodecodeint(
223254359Sroberto	char *str,
223354359Sroberto	long *val
223454359Sroberto	)
223554359Sroberto{
223654359Sroberto	if (*str == '0') {
223754359Sroberto		if (*(str+1) == 'x' || *(str+1) == 'X')
2238285612Sdelphij		    return hextoint(str+2, (u_long *)val);
2239285612Sdelphij		return octtoint(str, (u_long *)val);
224054359Sroberto	}
224154359Sroberto	return atoint(str, val);
224254359Sroberto}
224354359Sroberto
224454359Sroberto
224554359Sroberto/*
224654359Sroberto * decodeuint - decode an unsigned integer
224754359Sroberto */
224854359Srobertoint
224954359Srobertodecodeuint(
225054359Sroberto	char *str,
225154359Sroberto	u_long *val
225254359Sroberto	)
225354359Sroberto{
225454359Sroberto	if (*str == '0') {
225554359Sroberto		if (*(str + 1) == 'x' || *(str + 1) == 'X')
225654359Sroberto			return (hextoint(str + 2, val));
225754359Sroberto		return (octtoint(str, val));
225854359Sroberto	}
225954359Sroberto	return (atouint(str, val));
226054359Sroberto}
226154359Sroberto
226254359Sroberto
226354359Sroberto/*
226454359Sroberto * decodearr - decode an array of time values
226554359Sroberto */
226654359Srobertostatic int
226754359Srobertodecodearr(
2268330141Sdelphij	char *cp,
2269330141Sdelphij	int  *narr,
2270330141Sdelphij	l_fp *lfpa,
2271330141Sdelphij	int   amax
227254359Sroberto	)
227354359Sroberto{
2274330141Sdelphij	char *bp;
227554359Sroberto	char buf[60];
227654359Sroberto
227754359Sroberto	*narr = 0;
227854359Sroberto
2279330141Sdelphij	while (*narr < amax && *cp) {
2280330141Sdelphij		if (isspace(pgetc(cp))) {
2281330141Sdelphij			do
2282330141Sdelphij				++cp;
2283330141Sdelphij			while (*cp && isspace(pgetc(cp)));
2284330141Sdelphij		} else {
2285330141Sdelphij			bp = buf;
2286330141Sdelphij			do {
2287330141Sdelphij				if (bp != (buf + sizeof(buf) - 1))
2288330141Sdelphij					*bp++ = *cp;
2289330141Sdelphij				++cp;
2290330141Sdelphij			} while (*cp && !isspace(pgetc(cp)));
2291330141Sdelphij			*bp = '\0';
229254359Sroberto
2293330141Sdelphij			if (!decodetime(buf, lfpa))
2294330141Sdelphij				return 0;
2295330141Sdelphij			++(*narr);
2296330141Sdelphij			++lfpa;
2297330141Sdelphij		}
229854359Sroberto	}
229954359Sroberto	return 1;
230054359Sroberto}
230154359Sroberto
230254359Sroberto
230354359Sroberto/*
230454359Sroberto * Finally, the built in command handlers
230554359Sroberto */
230654359Sroberto
230754359Sroberto/*
230854359Sroberto * help - tell about commands, or details of a particular command
230954359Sroberto */
231054359Srobertostatic void
231154359Srobertohelp(
231254359Sroberto	struct parse *pcmd,
231354359Sroberto	FILE *fp
231454359Sroberto	)
231554359Sroberto{
2316285612Sdelphij	struct xcmd *xcp = NULL;	/* quiet warning */
2317285612Sdelphij	const char *cmd;
2318182007Sroberto	const char *list[100];
2319285612Sdelphij	size_t word, words;
2320285612Sdelphij	size_t row, rows;
2321285612Sdelphij	size_t col, cols;
2322285612Sdelphij	size_t length;
232354359Sroberto
232454359Sroberto	if (pcmd->nargs == 0) {
2325182007Sroberto		words = 0;
2326285612Sdelphij		for (xcp = builtins; xcp->keyword != NULL; xcp++) {
2327285612Sdelphij			if (*(xcp->keyword) != '?' &&
2328285612Sdelphij			    words < COUNTOF(list))
2329285612Sdelphij				list[words++] = xcp->keyword;
233054359Sroberto		}
2331285612Sdelphij		for (xcp = opcmds; xcp->keyword != NULL; xcp++)
2332285612Sdelphij			if (words < COUNTOF(list))
2333285612Sdelphij				list[words++] = xcp->keyword;
233454359Sroberto
2335285612Sdelphij		qsort((void *)list, words, sizeof(list[0]), helpsort);
2336182007Sroberto		col = 0;
2337182007Sroberto		for (word = 0; word < words; word++) {
2338285612Sdelphij			length = strlen(list[word]);
2339285612Sdelphij			col = max(col, length);
234054359Sroberto		}
234154359Sroberto
2342182007Sroberto		cols = SCREENWIDTH / ++col;
2343285612Sdelphij		rows = (words + cols - 1) / cols;
2344182007Sroberto
2345285612Sdelphij		fprintf(fp, "ntpq commands:\n");
2346182007Sroberto
2347182007Sroberto		for (row = 0; row < rows; row++) {
2348285612Sdelphij			for (word = row; word < words; word += rows)
2349285612Sdelphij				fprintf(fp, "%-*.*s", (int)col,
2350285612Sdelphij					(int)col - 1, list[word]);
2351285612Sdelphij			fprintf(fp, "\n");
2352285612Sdelphij		}
235354359Sroberto	} else {
235454359Sroberto		cmd = pcmd->argval[0].string;
2355182007Sroberto		words = findcmd(cmd, builtins, opcmds, &xcp);
2356182007Sroberto		if (words == 0) {
2357285612Sdelphij			fprintf(stderr,
2358285612Sdelphij				"Command `%s' is unknown\n", cmd);
235954359Sroberto			return;
2360182007Sroberto		} else if (words >= 2) {
2361285612Sdelphij			fprintf(stderr,
2362285612Sdelphij				"Command `%s' is ambiguous\n", cmd);
236354359Sroberto			return;
236454359Sroberto		}
2365285612Sdelphij		fprintf(fp, "function: %s\n", xcp->comment);
236654359Sroberto		printusage(xcp, fp);
236754359Sroberto	}
236854359Sroberto}
236954359Sroberto
237054359Sroberto
237154359Sroberto/*
237254359Sroberto * helpsort - do hostname qsort comparisons
237354359Sroberto */
237454359Srobertostatic int
237554359Srobertohelpsort(
237654359Sroberto	const void *t1,
237754359Sroberto	const void *t2
237854359Sroberto	)
237954359Sroberto{
2380285612Sdelphij	const char * const *	name1 = t1;
2381285612Sdelphij	const char * const *	name2 = t2;
238254359Sroberto
238354359Sroberto	return strcmp(*name1, *name2);
238454359Sroberto}
238554359Sroberto
238654359Sroberto
238754359Sroberto/*
238854359Sroberto * printusage - print usage information for a command
238954359Sroberto */
239054359Srobertostatic void
239154359Srobertoprintusage(
239254359Sroberto	struct xcmd *xcp,
239354359Sroberto	FILE *fp
239454359Sroberto	)
239554359Sroberto{
239654359Sroberto	register int i;
239754359Sroberto
2398285612Sdelphij	/* XXX: Do we need to warn about extra args here too? */
2399285612Sdelphij
240054359Sroberto	(void) fprintf(fp, "usage: %s", xcp->keyword);
240154359Sroberto	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
240254359Sroberto		if (xcp->arg[i] & OPT)
240354359Sroberto		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
240454359Sroberto		else
240554359Sroberto		    (void) fprintf(fp, " %s", xcp->desc[i]);
240654359Sroberto	}
240754359Sroberto	(void) fprintf(fp, "\n");
240854359Sroberto}
240954359Sroberto
241054359Sroberto
241154359Sroberto/*
241254359Sroberto * timeout - set time out time
241354359Sroberto */
241454359Srobertostatic void
241554359Srobertotimeout(
241654359Sroberto	struct parse *pcmd,
241754359Sroberto	FILE *fp
241854359Sroberto	)
241954359Sroberto{
242054359Sroberto	int val;
242154359Sroberto
242254359Sroberto	if (pcmd->nargs == 0) {
2423285612Sdelphij		val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
242454359Sroberto		(void) fprintf(fp, "primary timeout %d ms\n", val);
242554359Sroberto	} else {
242654359Sroberto		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2427285612Sdelphij		tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
242854359Sroberto			* 1000;
242954359Sroberto	}
243054359Sroberto}
243154359Sroberto
243254359Sroberto
243354359Sroberto/*
243454359Sroberto * auth_delay - set delay for auth requests
243554359Sroberto */
243654359Srobertostatic void
243754359Srobertoauth_delay(
243854359Sroberto	struct parse *pcmd,
243954359Sroberto	FILE *fp
244054359Sroberto	)
244154359Sroberto{
244254359Sroberto	int isneg;
244354359Sroberto	u_long val;
244454359Sroberto
244554359Sroberto	if (pcmd->nargs == 0) {
244654359Sroberto		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
244754359Sroberto		(void) fprintf(fp, "delay %lu ms\n", val);
244854359Sroberto	} else {
244954359Sroberto		if (pcmd->argval[0].ival < 0) {
245054359Sroberto			isneg = 1;
245154359Sroberto			val = (u_long)(-pcmd->argval[0].ival);
245254359Sroberto		} else {
245354359Sroberto			isneg = 0;
245454359Sroberto			val = (u_long)pcmd->argval[0].ival;
245554359Sroberto		}
245654359Sroberto
245754359Sroberto		delay_time.l_ui = val / 1000;
245854359Sroberto		val %= 1000;
245954359Sroberto		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
246054359Sroberto
246154359Sroberto		if (isneg)
246254359Sroberto		    L_NEG(&delay_time);
246354359Sroberto	}
246454359Sroberto}
246554359Sroberto
246654359Sroberto
246754359Sroberto/*
246854359Sroberto * host - set the host we are dealing with.
246954359Sroberto */
247054359Srobertostatic void
247154359Srobertohost(
247254359Sroberto	struct parse *pcmd,
247354359Sroberto	FILE *fp
247454359Sroberto	)
247554359Sroberto{
2476132451Sroberto	int i;
2477132451Sroberto
247854359Sroberto	if (pcmd->nargs == 0) {
247954359Sroberto		if (havehost)
2480285612Sdelphij			(void) fprintf(fp, "current host is %s\n",
2481285612Sdelphij					   currenthost);
248254359Sroberto		else
2483285612Sdelphij			(void) fprintf(fp, "no current host\n");
2484132451Sroberto		return;
2485132451Sroberto	}
2486132451Sroberto
2487132451Sroberto	i = 0;
2488132451Sroberto	ai_fam_templ = ai_fam_default;
2489132451Sroberto	if (pcmd->nargs == 2) {
2490132451Sroberto		if (!strcmp("-4", pcmd->argval[i].string))
2491132451Sroberto			ai_fam_templ = AF_INET;
2492132451Sroberto		else if (!strcmp("-6", pcmd->argval[i].string))
2493132451Sroberto			ai_fam_templ = AF_INET6;
2494285612Sdelphij		else
2495285612Sdelphij			goto no_change;
2496132451Sroberto		i = 1;
2497132451Sroberto	}
2498285612Sdelphij	if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
2499285612Sdelphij		fprintf(fp, "current host set to %s\n", currenthost);
250054359Sroberto	} else {
2501285612Sdelphij    no_change:
250254359Sroberto		if (havehost)
2503285612Sdelphij			fprintf(fp, "current host remains %s\n",
2504285612Sdelphij				currenthost);
250554359Sroberto		else
2506285612Sdelphij			fprintf(fp, "still no current host\n");
250754359Sroberto	}
250854359Sroberto}
250954359Sroberto
251054359Sroberto
251154359Sroberto/*
251254359Sroberto * poll - do one (or more) polls of the host via NTP
251354359Sroberto */
251454359Sroberto/*ARGSUSED*/
251554359Srobertostatic void
251654359Srobertontp_poll(
251754359Sroberto	struct parse *pcmd,
251854359Sroberto	FILE *fp
251954359Sroberto	)
252054359Sroberto{
252154359Sroberto	(void) fprintf(fp, "poll not implemented yet\n");
252254359Sroberto}
252354359Sroberto
252454359Sroberto
252554359Sroberto/*
2526298699Sdelphij * showdrefid2str - return a string explanation of the value of drefid
2527298699Sdelphij */
2528338531Sdelphijstatic const char *
2529298699Sdelphijshowdrefid2str(void)
2530298699Sdelphij{
2531298699Sdelphij	switch (drefid) {
2532298699Sdelphij	    case REFID_HASH:
2533298699Sdelphij	    	return "hash";
2534298699Sdelphij	    case REFID_IPV4:
2535298699Sdelphij	    	return "ipv4";
2536298699Sdelphij	    default:
2537298699Sdelphij	    	return "Unknown";
2538298699Sdelphij	}
2539298699Sdelphij}
2540298699Sdelphij
2541298699Sdelphij
2542298699Sdelphij/*
2543298699Sdelphij * drefid - display/change "display hash"
2544298699Sdelphij */
2545298699Sdelphijstatic void
2546298699Sdelphijshowdrefid(
2547298699Sdelphij	struct parse *pcmd,
2548298699Sdelphij	FILE *fp
2549298699Sdelphij	)
2550298699Sdelphij{
2551298699Sdelphij	if (pcmd->nargs == 0) {
2552298699Sdelphij		(void) fprintf(fp, "drefid value is %s\n", showdrefid2str());
2553298699Sdelphij		return;
2554298699Sdelphij	} else if (STREQ(pcmd->argval[0].string, "hash")) {
2555298699Sdelphij		drefid = REFID_HASH;
2556298699Sdelphij	} else if (STREQ(pcmd->argval[0].string, "ipv4")) {
2557298699Sdelphij		drefid = REFID_IPV4;
2558298699Sdelphij	} else {
2559298699Sdelphij		(void) fprintf(fp, "What?\n");
2560298699Sdelphij		return;
2561298699Sdelphij	}
2562298699Sdelphij	(void) fprintf(fp, "drefid value set to %s\n", showdrefid2str());
2563298699Sdelphij}
2564298699Sdelphij
2565298699Sdelphij
2566298699Sdelphij/*
256754359Sroberto * keyid - get a keyid to use for authenticating requests
256854359Sroberto */
256954359Srobertostatic void
257054359Srobertokeyid(
257154359Sroberto	struct parse *pcmd,
257254359Sroberto	FILE *fp
257354359Sroberto	)
257454359Sroberto{
257554359Sroberto	if (pcmd->nargs == 0) {
2576132451Sroberto		if (info_auth_keyid == 0)
257754359Sroberto		    (void) fprintf(fp, "no keyid defined\n");
257854359Sroberto		else
257954359Sroberto		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
258054359Sroberto	} else {
2581132451Sroberto		/* allow zero so that keyid can be cleared. */
2582132451Sroberto		if(pcmd->argval[0].uval > NTP_MAXKEY)
2583132451Sroberto		    (void) fprintf(fp, "Invalid key identifier\n");
258454359Sroberto		info_auth_keyid = pcmd->argval[0].uval;
258554359Sroberto	}
258654359Sroberto}
258754359Sroberto
258854359Sroberto/*
258954359Sroberto * keytype - get type of key to use for authenticating requests
259054359Sroberto */
259154359Srobertostatic void
259254359Srobertokeytype(
259354359Sroberto	struct parse *pcmd,
259454359Sroberto	FILE *fp
259554359Sroberto	)
259654359Sroberto{
2597285612Sdelphij	const char *	digest_name;
2598285612Sdelphij	size_t		digest_len;
2599285612Sdelphij	int		key_type;
260054359Sroberto
2601285612Sdelphij	if (!pcmd->nargs) {
2602285612Sdelphij		fprintf(fp, "keytype is %s with %lu octet digests\n",
2603285612Sdelphij			keytype_name(info_auth_keytype),
2604285612Sdelphij			(u_long)info_auth_hashlen);
2605285612Sdelphij		return;
2606285612Sdelphij	}
2607285612Sdelphij
2608285612Sdelphij	digest_name = pcmd->argval[0].string;
2609285612Sdelphij	digest_len = 0;
2610285612Sdelphij	key_type = keytype_from_text(digest_name, &digest_len);
2611285612Sdelphij
2612285612Sdelphij	if (!key_type) {
2613285612Sdelphij		fprintf(fp, "keytype is not valid. "
2614285612Sdelphij#ifdef OPENSSL
2615285612Sdelphij			"Type \"help keytype\" for the available digest types.\n");
2616285612Sdelphij#else
2617285612Sdelphij			"Only \"md5\" is available.\n");
2618285612Sdelphij#endif
2619285612Sdelphij		return;
2620285612Sdelphij	}
2621285612Sdelphij
2622285612Sdelphij	info_auth_keytype = key_type;
2623285612Sdelphij	info_auth_hashlen = digest_len;
262454359Sroberto}
262554359Sroberto
262654359Sroberto
262754359Sroberto/*
262854359Sroberto * passwd - get an authentication key
262954359Sroberto */
263054359Sroberto/*ARGSUSED*/
263154359Srobertostatic void
263254359Srobertopasswd(
263354359Sroberto	struct parse *pcmd,
263454359Sroberto	FILE *fp
263554359Sroberto	)
263654359Sroberto{
2637285612Sdelphij	const char *pass;
263854359Sroberto
2639132451Sroberto	if (info_auth_keyid == 0) {
2640285612Sdelphij		info_auth_keyid = getkeyid("Keyid: ");
2641285612Sdelphij		if (info_auth_keyid == 0) {
2642285612Sdelphij			(void)fprintf(fp, "Keyid must be defined\n");
264354359Sroberto			return;
264454359Sroberto		}
264554359Sroberto	}
2646285612Sdelphij	if (pcmd->nargs >= 1)
2647285612Sdelphij		pass = pcmd->argval[0].string;
2648132451Sroberto	else {
2649285612Sdelphij		pass = getpass_keytype(info_auth_keytype);
2650285612Sdelphij		if ('\0' == pass[0]) {
2651285612Sdelphij			fprintf(fp, "Password unchanged\n");
2652285612Sdelphij			return;
2653285612Sdelphij		}
2654132451Sroberto	}
2655285612Sdelphij	authusekey(info_auth_keyid, info_auth_keytype,
2656285612Sdelphij		   (const u_char *)pass);
2657285612Sdelphij	authtrust(info_auth_keyid, 1);
265854359Sroberto}
265954359Sroberto
266054359Sroberto
266154359Sroberto/*
266254359Sroberto * hostnames - set the showhostnames flag
266354359Sroberto */
266454359Srobertostatic void
266554359Srobertohostnames(
266654359Sroberto	struct parse *pcmd,
266754359Sroberto	FILE *fp
266854359Sroberto	)
266954359Sroberto{
267054359Sroberto	if (pcmd->nargs == 0) {
267154359Sroberto		if (showhostnames)
267254359Sroberto		    (void) fprintf(fp, "hostnames being shown\n");
267354359Sroberto		else
267454359Sroberto		    (void) fprintf(fp, "hostnames not being shown\n");
267554359Sroberto	} else {
267654359Sroberto		if (STREQ(pcmd->argval[0].string, "yes"))
267754359Sroberto		    showhostnames = 1;
267854359Sroberto		else if (STREQ(pcmd->argval[0].string, "no"))
267954359Sroberto		    showhostnames = 0;
268054359Sroberto		else
268154359Sroberto		    (void)fprintf(stderr, "What?\n");
268254359Sroberto	}
268354359Sroberto}
268454359Sroberto
268554359Sroberto
268654359Sroberto
268754359Sroberto/*
268854359Sroberto * setdebug - set/change debugging level
268954359Sroberto */
269054359Srobertostatic void
269154359Srobertosetdebug(
269254359Sroberto	struct parse *pcmd,
269354359Sroberto	FILE *fp
269454359Sroberto	)
269554359Sroberto{
269654359Sroberto	if (pcmd->nargs == 0) {
269754359Sroberto		(void) fprintf(fp, "debug level is %d\n", debug);
269854359Sroberto		return;
269954359Sroberto	} else if (STREQ(pcmd->argval[0].string, "no")) {
270054359Sroberto		debug = 0;
270154359Sroberto	} else if (STREQ(pcmd->argval[0].string, "more")) {
270254359Sroberto		debug++;
270354359Sroberto	} else if (STREQ(pcmd->argval[0].string, "less")) {
270454359Sroberto		debug--;
270554359Sroberto	} else {
270654359Sroberto		(void) fprintf(fp, "What?\n");
270754359Sroberto		return;
270854359Sroberto	}
270954359Sroberto	(void) fprintf(fp, "debug level set to %d\n", debug);
271054359Sroberto}
271154359Sroberto
271254359Sroberto
271354359Sroberto/*
271454359Sroberto * quit - stop this nonsense
271554359Sroberto */
271654359Sroberto/*ARGSUSED*/
271754359Srobertostatic void
271854359Srobertoquit(
271954359Sroberto	struct parse *pcmd,
272054359Sroberto	FILE *fp
272154359Sroberto	)
272254359Sroberto{
272354359Sroberto	if (havehost)
272454359Sroberto	    closesocket(sockfd);	/* cleanliness next to godliness */
272554359Sroberto	exit(0);
272654359Sroberto}
272754359Sroberto
272854359Sroberto
272954359Sroberto/*
273054359Sroberto * version - print the current version number
273154359Sroberto */
273254359Sroberto/*ARGSUSED*/
273354359Srobertostatic void
273454359Srobertoversion(
273554359Sroberto	struct parse *pcmd,
273654359Sroberto	FILE *fp
273754359Sroberto	)
273854359Sroberto{
273954359Sroberto
274054359Sroberto	(void) fprintf(fp, "%s\n", Version);
274154359Sroberto	return;
274254359Sroberto}
274354359Sroberto
274454359Sroberto
274554359Sroberto/*
274654359Sroberto * raw - set raw mode output
274754359Sroberto */
274854359Sroberto/*ARGSUSED*/
274954359Srobertostatic void
275054359Srobertoraw(
275154359Sroberto	struct parse *pcmd,
275254359Sroberto	FILE *fp
275354359Sroberto	)
275454359Sroberto{
275554359Sroberto	rawmode = 1;
275654359Sroberto	(void) fprintf(fp, "Output set to raw\n");
275754359Sroberto}
275854359Sroberto
275954359Sroberto
276054359Sroberto/*
276154359Sroberto * cooked - set cooked mode output
276254359Sroberto */
276354359Sroberto/*ARGSUSED*/
276454359Srobertostatic void
276554359Srobertocooked(
276654359Sroberto	struct parse *pcmd,
276754359Sroberto	FILE *fp
276854359Sroberto	)
276954359Sroberto{
277054359Sroberto	rawmode = 0;
277154359Sroberto	(void) fprintf(fp, "Output set to cooked\n");
277254359Sroberto	return;
277354359Sroberto}
277454359Sroberto
277554359Sroberto
277654359Sroberto/*
277754359Sroberto * authenticate - always authenticate requests to this host
277854359Sroberto */
277954359Srobertostatic void
278054359Srobertoauthenticate(
278154359Sroberto	struct parse *pcmd,
278254359Sroberto	FILE *fp
278354359Sroberto	)
278454359Sroberto{
278554359Sroberto	if (pcmd->nargs == 0) {
278654359Sroberto		if (always_auth) {
278754359Sroberto			(void) fprintf(fp,
278854359Sroberto				       "authenticated requests being sent\n");
278954359Sroberto		} else
279054359Sroberto		    (void) fprintf(fp,
279154359Sroberto				   "unauthenticated requests being sent\n");
279254359Sroberto	} else {
279354359Sroberto		if (STREQ(pcmd->argval[0].string, "yes")) {
279454359Sroberto			always_auth = 1;
279554359Sroberto		} else if (STREQ(pcmd->argval[0].string, "no")) {
279654359Sroberto			always_auth = 0;
279754359Sroberto		} else
279854359Sroberto		    (void)fprintf(stderr, "What?\n");
279954359Sroberto	}
280054359Sroberto}
280154359Sroberto
280254359Sroberto
280354359Sroberto/*
280454359Sroberto * ntpversion - choose the NTP version to use
280554359Sroberto */
280654359Srobertostatic void
280754359Srobertontpversion(
280854359Sroberto	struct parse *pcmd,
280954359Sroberto	FILE *fp
281054359Sroberto	)
281154359Sroberto{
281254359Sroberto	if (pcmd->nargs == 0) {
281354359Sroberto		(void) fprintf(fp,
281454359Sroberto			       "NTP version being claimed is %d\n", pktversion);
281554359Sroberto	} else {
281654359Sroberto		if (pcmd->argval[0].uval < NTP_OLDVERSION
281754359Sroberto		    || pcmd->argval[0].uval > NTP_VERSION) {
281854359Sroberto			(void) fprintf(stderr, "versions %d to %d, please\n",
281954359Sroberto				       NTP_OLDVERSION, NTP_VERSION);
282054359Sroberto		} else {
282154359Sroberto			pktversion = (u_char) pcmd->argval[0].uval;
282254359Sroberto		}
282354359Sroberto	}
282454359Sroberto}
282554359Sroberto
282654359Sroberto
2827285612Sdelphijstatic void __attribute__((__format__(__printf__, 1, 0)))
2828285612Sdelphijvwarning(const char *fmt, va_list ap)
2829285612Sdelphij{
2830285612Sdelphij	int serrno = errno;
2831285612Sdelphij	(void) fprintf(stderr, "%s: ", progname);
2832285612Sdelphij	vfprintf(stderr, fmt, ap);
2833293650Sglebius	(void) fprintf(stderr, ": %s\n", strerror(serrno));
2834285612Sdelphij}
2835285612Sdelphij
283654359Sroberto/*
283754359Sroberto * warning - print a warning message
283854359Sroberto */
2839285612Sdelphijstatic void __attribute__((__format__(__printf__, 1, 2)))
284054359Srobertowarning(
284154359Sroberto	const char *fmt,
2842285612Sdelphij	...
284354359Sroberto	)
284454359Sroberto{
2845285612Sdelphij	va_list ap;
2846285612Sdelphij	va_start(ap, fmt);
2847285612Sdelphij	vwarning(fmt, ap);
2848285612Sdelphij	va_end(ap);
284954359Sroberto}
285054359Sroberto
285154359Sroberto
285254359Sroberto/*
285354359Sroberto * error - print a message and exit
285454359Sroberto */
2855285612Sdelphijstatic void __attribute__((__format__(__printf__, 1, 2)))
285654359Srobertoerror(
285754359Sroberto	const char *fmt,
2858285612Sdelphij	...
285954359Sroberto	)
286054359Sroberto{
2861285612Sdelphij	va_list ap;
2862285612Sdelphij	va_start(ap, fmt);
2863285612Sdelphij	vwarning(fmt, ap);
2864285612Sdelphij	va_end(ap);
286554359Sroberto	exit(1);
286654359Sroberto}
286754359Sroberto/*
286854359Sroberto * getkeyid - prompt the user for a keyid to use
286954359Sroberto */
287054359Srobertostatic u_long
287154359Srobertogetkeyid(
287254359Sroberto	const char *keyprompt
287354359Sroberto	)
287454359Sroberto{
2875285612Sdelphij	int c;
287654359Sroberto	FILE *fi;
287754359Sroberto	char pbuf[20];
2878285612Sdelphij	size_t i;
2879285612Sdelphij	size_t ilim;
288054359Sroberto
288154359Sroberto#ifndef SYS_WINNT
288254359Sroberto	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
288354359Sroberto#else
2884285612Sdelphij	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
288554359Sroberto#endif /* SYS_WINNT */
288654359Sroberto		fi = stdin;
2887285612Sdelphij	else
288854359Sroberto		setbuf(fi, (char *)NULL);
288954359Sroberto	fprintf(stderr, "%s", keyprompt); fflush(stderr);
2890285612Sdelphij	for (i = 0, ilim = COUNTOF(pbuf) - 1;
2891285612Sdelphij	     i < ilim && (c = getc(fi)) != '\n' && c != EOF;
2892285612Sdelphij	     )
2893285612Sdelphij		pbuf[i++] = (char)c;
2894285612Sdelphij	pbuf[i] = '\0';
289554359Sroberto	if (fi != stdin)
2896285612Sdelphij		fclose(fi);
289754359Sroberto
289854359Sroberto	return (u_long) atoi(pbuf);
289954359Sroberto}
290054359Sroberto
290154359Sroberto
290254359Sroberto/*
290354359Sroberto * atoascii - printable-ize possibly ascii data using the character
290454359Sroberto *	      transformations cat -v uses.
290554359Sroberto */
290654359Srobertostatic void
290754359Srobertoatoascii(
2908285612Sdelphij	const char *in,
2909285612Sdelphij	size_t in_octets,
2910285612Sdelphij	char *out,
2911285612Sdelphij	size_t out_octets
291254359Sroberto	)
291354359Sroberto{
2914285612Sdelphij	const u_char *	pchIn;
2915285612Sdelphij	const u_char *	pchInLimit;
2916285612Sdelphij	u_char *	pchOut;
2917285612Sdelphij	u_char		c;
291854359Sroberto
2919285612Sdelphij	pchIn = (const u_char *)in;
2920285612Sdelphij	pchInLimit = pchIn + in_octets;
2921285612Sdelphij	pchOut = (u_char *)out;
2922285612Sdelphij
2923285612Sdelphij	if (NULL == pchIn) {
2924285612Sdelphij		if (0 < out_octets)
2925285612Sdelphij			*pchOut = '\0';
292654359Sroberto		return;
292754359Sroberto	}
292854359Sroberto
2929285612Sdelphij#define	ONEOUT(c)					\
2930285612Sdelphijdo {							\
2931285612Sdelphij	if (0 == --out_octets) {			\
2932285612Sdelphij		*pchOut = '\0';				\
2933285612Sdelphij		return;					\
2934285612Sdelphij	}						\
2935285612Sdelphij	*pchOut++ = (c);				\
2936285612Sdelphij} while (0)
2937285612Sdelphij
2938285612Sdelphij	for (	; pchIn < pchInLimit; pchIn++) {
2939285612Sdelphij		c = *pchIn;
2940285612Sdelphij		if ('\0' == c)
2941285612Sdelphij			break;
2942285612Sdelphij		if (c & 0x80) {
2943285612Sdelphij			ONEOUT('M');
2944285612Sdelphij			ONEOUT('-');
2945285612Sdelphij			c &= 0x7f;
294654359Sroberto		}
294754359Sroberto		if (c < ' ') {
2948285612Sdelphij			ONEOUT('^');
2949285612Sdelphij			ONEOUT((u_char)(c + '@'));
2950285612Sdelphij		} else if (0x7f == c) {
2951285612Sdelphij			ONEOUT('^');
2952285612Sdelphij			ONEOUT('?');
2953285612Sdelphij		} else
2954285612Sdelphij			ONEOUT(c);
295554359Sroberto	}
2956285612Sdelphij	ONEOUT('\0');
2957285612Sdelphij
2958285612Sdelphij#undef ONEOUT
295954359Sroberto}
296054359Sroberto
296154359Sroberto
296254359Sroberto/*
296354359Sroberto * makeascii - print possibly ascii data using the character
296454359Sroberto *	       transformations that cat -v uses.
296554359Sroberto */
2966285612Sdelphijvoid
296754359Srobertomakeascii(
2968293650Sglebius	size_t length,
2969285612Sdelphij	const char *data,
297054359Sroberto	FILE *fp
297154359Sroberto	)
297254359Sroberto{
2973285612Sdelphij	const u_char *data_u_char;
2974285612Sdelphij	const u_char *cp;
2975285612Sdelphij	int c;
297654359Sroberto
2977285612Sdelphij	data_u_char = (const u_char *)data;
2978285612Sdelphij
2979285612Sdelphij	for (cp = data_u_char; cp < data_u_char + length; cp++) {
298054359Sroberto		c = (int)*cp;
2981285612Sdelphij		if (c & 0x80) {
298254359Sroberto			putc('M', fp);
298354359Sroberto			putc('-', fp);
2984285612Sdelphij			c &= 0x7f;
298554359Sroberto		}
298654359Sroberto
298754359Sroberto		if (c < ' ') {
298854359Sroberto			putc('^', fp);
2989285612Sdelphij			putc(c + '@', fp);
2990285612Sdelphij		} else if (0x7f == c) {
299154359Sroberto			putc('^', fp);
299254359Sroberto			putc('?', fp);
2993285612Sdelphij		} else
299454359Sroberto			putc(c, fp);
299554359Sroberto	}
299654359Sroberto}
299754359Sroberto
299854359Sroberto
299954359Sroberto/*
300054359Sroberto * asciize - same thing as makeascii except add a newline
300154359Sroberto */
300254359Srobertovoid
300354359Srobertoasciize(
300454359Sroberto	int length,
300554359Sroberto	char *data,
300654359Sroberto	FILE *fp
300754359Sroberto	)
300854359Sroberto{
300954359Sroberto	makeascii(length, data, fp);
301054359Sroberto	putc('\n', fp);
301154359Sroberto}
301254359Sroberto
301354359Sroberto
301454359Sroberto/*
3015285612Sdelphij * truncate string to fit clipping excess at end.
3016285612Sdelphij *	"too long"	->	"too l"
3017285612Sdelphij * Used for hostnames.
3018285612Sdelphij */
3019285612Sdelphijconst char *
3020285612Sdelphijtrunc_right(
3021285612Sdelphij	const char *	src,
3022285612Sdelphij	size_t		width
3023285612Sdelphij	)
3024285612Sdelphij{
3025285612Sdelphij	size_t	sl;
3026285612Sdelphij	char *	out;
3027285612Sdelphij
3028285612Sdelphij
3029285612Sdelphij	sl = strlen(src);
3030285612Sdelphij	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
3031285612Sdelphij		LIB_GETBUF(out);
3032285612Sdelphij		memcpy(out, src, width);
3033285612Sdelphij		out[width] = '\0';
3034285612Sdelphij
3035285612Sdelphij		return out;
3036285612Sdelphij	}
3037285612Sdelphij
3038285612Sdelphij	return src;
3039285612Sdelphij}
3040285612Sdelphij
3041285612Sdelphij
3042285612Sdelphij/*
3043285612Sdelphij * truncate string to fit by preserving right side and using '_' to hint
3044285612Sdelphij *	"too long"	->	"_long"
3045285612Sdelphij * Used for local IPv6 addresses, where low bits differentiate.
3046285612Sdelphij */
3047285612Sdelphijconst char *
3048285612Sdelphijtrunc_left(
3049285612Sdelphij	const char *	src,
3050285612Sdelphij	size_t		width
3051285612Sdelphij	)
3052285612Sdelphij{
3053285612Sdelphij	size_t	sl;
3054285612Sdelphij	char *	out;
3055285612Sdelphij
3056285612Sdelphij
3057285612Sdelphij	sl = strlen(src);
3058285612Sdelphij	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
3059285612Sdelphij		LIB_GETBUF(out);
3060285612Sdelphij		out[0] = '_';
3061285612Sdelphij		memcpy(&out[1], &src[sl + 1 - width], width);
3062285612Sdelphij
3063285612Sdelphij		return out;
3064285612Sdelphij	}
3065285612Sdelphij
3066285612Sdelphij	return src;
3067285612Sdelphij}
3068285612Sdelphij
3069285612Sdelphij
3070285612Sdelphij/*
307154359Sroberto * Some circular buffer space
307254359Sroberto */
307354359Sroberto#define	CBLEN	80
307454359Sroberto#define	NUMCB	6
307554359Sroberto
307654359Srobertochar circ_buf[NUMCB][CBLEN];
307754359Srobertoint nextcb = 0;
307854359Sroberto
3079338531Sdelphij/* --------------------------------------------------------------------
3080338531Sdelphij * Parsing a response value list
3081338531Sdelphij *
3082338531Sdelphij * This sounds simple (and it actually is not really hard) but it has
3083338531Sdelphij * some pitfalls.
3084338531Sdelphij *
3085338531Sdelphij * Rule1: CR/LF is never embedded in an item
3086338531Sdelphij * Rule2: An item is a name, optionally followed by a value
3087338531Sdelphij * Rule3: The value is separated from the name by a '='
3088338531Sdelphij * Rule4: Items are separated by a ','
3089338531Sdelphij * Rule5: values can be quoted by '"', in which case they can contain
3090338531Sdelphij *        arbitrary characters but *not* '"', CR and LF
3091338531Sdelphij *
3092338531Sdelphij * There are a few implementations out there that require a somewhat
3093338531Sdelphij * relaxed attitude when parsing a value list, especially since we want
3094338531Sdelphij * to copy names and values into local buffers. If these would overflow,
3095338531Sdelphij * the item should be skipped without terminating the parsing sequence.
3096338531Sdelphij *
3097338531Sdelphij * Also, for empty values, there might be a '=' after the name or not;
3098338531Sdelphij * we treat that equivalent.
3099338531Sdelphij *
3100338531Sdelphij * Parsing an item definitely breaks on a CR/LF. If an item is not
3101338531Sdelphij * followed by a comma (','), parsing stops. In the middle of a quoted
3102338531Sdelphij * character sequence CR/LF terminates the parsing finally without
3103338531Sdelphij * returning a value.
3104338531Sdelphij *
3105338531Sdelphij * White space and other noise is ignored when parsing the data buffer;
3106338531Sdelphij * only CR, LF, ',', '=' and '"' are characters with a special meaning.
3107338531Sdelphij * White space is stripped from the names and values *after* working
3108338531Sdelphij * through the buffer, before making the local copies. If whitespace
3109338531Sdelphij * stripping results in an empty name, parsing resumes.
3110338531Sdelphij */
3111338531Sdelphij
311254359Sroberto/*
3113338531Sdelphij * nextvar parsing helpers
3114338531Sdelphij */
3115338531Sdelphij
3116338531Sdelphij/* predicate: allowed chars inside a quoted string */
3117338531Sdelphijstatic int/*BOOL*/ cp_qschar(int ch)
3118338531Sdelphij{
3119338531Sdelphij	return ch && (ch != '"' && ch != '\r' && ch != '\n');
3120338531Sdelphij}
3121338531Sdelphij
3122338531Sdelphij/* predicate: allowed chars inside an unquoted string */
3123338531Sdelphijstatic int/*BOOL*/ cp_uqchar(int ch)
3124338531Sdelphij{
3125338531Sdelphij	return ch && (ch != ',' && ch != '"' && ch != '\r' && ch != '\n');
3126338531Sdelphij}
3127338531Sdelphij
3128338531Sdelphij/* predicate: allowed chars inside a value name */
3129338531Sdelphijstatic int/*BOOL*/ cp_namechar(int ch)
3130338531Sdelphij{
3131338531Sdelphij	return ch && (ch != ',' && ch != '=' && ch != '\r' && ch != '\n');
3132338531Sdelphij}
3133338531Sdelphij
3134338531Sdelphij/* predicate: characters *between* list items. We're relaxed here. */
3135338531Sdelphijstatic int/*BOOL*/ cp_ivspace(int ch)
3136338531Sdelphij{
3137338531Sdelphij	return (ch == ',' || (ch > 0 && ch <= ' '));
3138338531Sdelphij}
3139338531Sdelphij
3140338531Sdelphij/* get current character (or NUL when on end) */
3141338531Sdelphijstatic inline int
3142338531Sdelphijpf_getch(
3143338531Sdelphij	const char **	datap,
3144338531Sdelphij	const char *	endp
3145338531Sdelphij	)
3146338531Sdelphij{
3147338531Sdelphij	return (*datap != endp)
3148338531Sdelphij	    ? *(const unsigned char*)*datap
3149338531Sdelphij	    : '\0';
3150338531Sdelphij}
3151338531Sdelphij
3152338531Sdelphij/* get next character (or NUL when on end) */
3153338531Sdelphijstatic inline int
3154338531Sdelphijpf_nextch(
3155338531Sdelphij	const char **	datap,
3156338531Sdelphij	const char *	endp
3157338531Sdelphij	)
3158338531Sdelphij{
3159338531Sdelphij	return (*datap != endp && ++(*datap) != endp)
3160338531Sdelphij	    ? *(const unsigned char*)*datap
3161338531Sdelphij	    : '\0';
3162338531Sdelphij}
3163338531Sdelphij
3164338531Sdelphijstatic size_t
3165338531Sdelphijstr_strip(
3166338531Sdelphij	const char ** 	datap,
3167338531Sdelphij	size_t		len
3168338531Sdelphij	)
3169338531Sdelphij{
3170338531Sdelphij	static const char empty[] = "";
3171338531Sdelphij
3172338531Sdelphij	if (*datap && len) {
3173338531Sdelphij		const char * cpl = *datap;
3174338531Sdelphij		const char * cpr = cpl + len;
3175338531Sdelphij
3176338531Sdelphij		while (cpl != cpr && *(const unsigned char*)cpl <= ' ')
3177338531Sdelphij			++cpl;
3178338531Sdelphij		while (cpl != cpr && *(const unsigned char*)(cpr - 1) <= ' ')
3179338531Sdelphij			--cpr;
3180338531Sdelphij		*datap = cpl;
3181338531Sdelphij		len = (size_t)(cpr - cpl);
3182338531Sdelphij	} else {
3183338531Sdelphij		*datap = empty;
3184338531Sdelphij		len = 0;
3185338531Sdelphij	}
3186338531Sdelphij	return len;
3187338531Sdelphij}
3188338531Sdelphij
3189338531Sdelphijstatic void
3190338531Sdelphijpf_error(
3191338531Sdelphij	const char *	what,
3192338531Sdelphij	const char *	where,
3193338531Sdelphij	const char *	whend
3194338531Sdelphij	)
3195338531Sdelphij{
3196338531Sdelphij#   ifndef BUILD_AS_LIB
3197338531Sdelphij
3198338531Sdelphij	FILE *	ofp = (debug > 0) ? stdout : stderr;
3199338531Sdelphij	size_t	len = (size_t)(whend - where);
3200338531Sdelphij
3201338531Sdelphij	if (len > 50) /* *must* fit into an 'int'! */
3202338531Sdelphij		len = 50;
3203338531Sdelphij	fprintf(ofp, "nextvar: %s: '%.*s'\n",
3204338531Sdelphij		what, (int)len, where);
3205338531Sdelphij
3206338531Sdelphij#   else  /*defined(BUILD_AS_LIB)*/
3207338531Sdelphij
3208338531Sdelphij	UNUSED_ARG(what);
3209338531Sdelphij	UNUSED_ARG(where);
3210338531Sdelphij	UNUSED_ARG(whend);
3211338531Sdelphij
3212338531Sdelphij#   endif /*defined(BUILD_AS_LIB)*/
3213338531Sdelphij}
3214338531Sdelphij
3215338531Sdelphij/*
321654359Sroberto * nextvar - find the next variable in the buffer
321754359Sroberto */
3218338531Sdelphijint/*BOOL*/
321954359Srobertonextvar(
3220293650Sglebius	size_t *datalen,
3221285612Sdelphij	const char **datap,
322254359Sroberto	char **vname,
322354359Sroberto	char **vvalue
322454359Sroberto	)
322554359Sroberto{
3226338531Sdelphij	enum PState 	{ sDone, sInit, sName, sValU, sValQ };
3227338531Sdelphij
3228338531Sdelphij	static char	name[MAXVARLEN], value[MAXVALLEN];
322954359Sroberto
3230338531Sdelphij	const char	*cp, *cpend;
3231338531Sdelphij	const char	*np, *vp;
3232338531Sdelphij	size_t		nlen, vlen;
3233338531Sdelphij	int		ch;
3234338531Sdelphij	enum PState	st;
3235338531Sdelphij
3236338531Sdelphij	cpend = *datap + *datalen;
323754359Sroberto
3238338531Sdelphij  again:
3239338531Sdelphij	np   = vp   = NULL;
3240338531Sdelphij	nlen = vlen = 0;
3241338531Sdelphij
3242338531Sdelphij	st = sInit;
3243338531Sdelphij	ch = pf_getch(datap, cpend);
3244285612Sdelphij
3245338531Sdelphij	while (st != sDone) {
3246338531Sdelphij		switch (st)
3247338531Sdelphij		{
3248338531Sdelphij		case sInit:	/* handle inter-item chars */
3249338531Sdelphij			while (cp_ivspace(ch))
3250338531Sdelphij				ch = pf_nextch(datap, cpend);
3251338531Sdelphij			if (cp_namechar(ch)) {
3252338531Sdelphij				np = *datap;
3253338531Sdelphij				cp = np;
3254338531Sdelphij				st = sName;
3255338531Sdelphij				ch = pf_nextch(datap, cpend);
3256338531Sdelphij			} else {
3257338531Sdelphij				goto final_done;
3258338531Sdelphij			}
3259338531Sdelphij			break;
3260338531Sdelphij
3261338531Sdelphij		case sName:	/* collect name */
3262338531Sdelphij			while (cp_namechar(ch))
3263338531Sdelphij				ch = pf_nextch(datap, cpend);
3264338531Sdelphij			nlen = (size_t)(*datap - np);
3265338531Sdelphij			if (ch == '=') {
3266338531Sdelphij				ch = pf_nextch(datap, cpend);
3267338531Sdelphij				vp = *datap;
3268338531Sdelphij				st = sValU;
3269338531Sdelphij			} else {
3270338531Sdelphij				if (ch != ',')
3271338531Sdelphij					*datap = cpend;
3272338531Sdelphij				st = sDone;
3273338531Sdelphij			}
3274338531Sdelphij			break;
3275338531Sdelphij
3276338531Sdelphij		case sValU:	/* collect unquoted part(s) of value */
3277338531Sdelphij			while (cp_uqchar(ch))
3278338531Sdelphij				ch = pf_nextch(datap, cpend);
3279338531Sdelphij			if (ch == '"') {
3280338531Sdelphij				ch = pf_nextch(datap, cpend);
3281338531Sdelphij				st = sValQ;
3282338531Sdelphij			} else {
3283338531Sdelphij				vlen = (size_t)(*datap - vp);
3284338531Sdelphij				if (ch != ',')
3285338531Sdelphij					*datap = cpend;
3286338531Sdelphij				st = sDone;
3287338531Sdelphij			}
3288338531Sdelphij			break;
3289338531Sdelphij
3290338531Sdelphij		case sValQ:	/* collect quoted part(s) of value */
3291338531Sdelphij			while (cp_qschar(ch))
3292338531Sdelphij				ch = pf_nextch(datap, cpend);
3293338531Sdelphij			if (ch == '"') {
3294338531Sdelphij				ch = pf_nextch(datap, cpend);
3295338531Sdelphij				st = sValU;
3296338531Sdelphij			} else {
3297338531Sdelphij				pf_error("no closing quote, stop", cp, cpend);
3298338531Sdelphij				goto final_done;
3299338531Sdelphij			}
3300338531Sdelphij			break;
3301338531Sdelphij
3302338531Sdelphij		default:
3303338531Sdelphij			pf_error("state machine error, stop", *datap, cpend);
3304338531Sdelphij			goto final_done;
3305338531Sdelphij		}
330654359Sroberto	}
330754359Sroberto
3308338531Sdelphij	/* If name or value do not fit their buffer, croak and start
3309338531Sdelphij	 * over. If there's no name at all after whitespace stripping,
3310338531Sdelphij	 * redo silently.
331154359Sroberto	 */
3312338531Sdelphij	nlen = str_strip(&np, nlen);
3313338531Sdelphij	vlen = str_strip(&vp, vlen);
3314338531Sdelphij
3315338531Sdelphij	if (nlen == 0) {
3316338531Sdelphij		goto again;
331754359Sroberto	}
3318338531Sdelphij	if (nlen >= sizeof(name)) {
3319338531Sdelphij		pf_error("runaway name", np, cpend);
3320338531Sdelphij		goto again;
3321338531Sdelphij	}
3322338531Sdelphij	if (vlen >= sizeof(value)) {
3323338531Sdelphij		pf_error("runaway value", vp, cpend);
3324338531Sdelphij		goto again;
3325338531Sdelphij	}
332654359Sroberto
3327338531Sdelphij	/* copy name and value into NUL-terminated buffers */
3328338531Sdelphij	memcpy(name, np, nlen);
3329338531Sdelphij	name[nlen] = '\0';
3330338531Sdelphij	*vname = name;
3331338531Sdelphij
3332338531Sdelphij	memcpy(value, vp, vlen);
3333338531Sdelphij	value[vlen] = '\0';
333454359Sroberto	*vvalue = value;
3335338531Sdelphij
3336338531Sdelphij	/* check if there's more to do or if we are finshed */
3337338531Sdelphij	*datalen = (size_t)(cpend - *datap);
3338338531Sdelphij	return TRUE;
3339338531Sdelphij
3340338531Sdelphij  final_done:
3341338531Sdelphij	*datap = cpend;
3342338531Sdelphij	*datalen = 0;
3343338531Sdelphij	return FALSE;
334454359Sroberto}
334554359Sroberto
334654359Sroberto
3347285612Sdelphiju_short
3348285612Sdelphijvarfmt(const char * varname)
334954359Sroberto{
3350285612Sdelphij	u_int n;
335154359Sroberto
3352285612Sdelphij	for (n = 0; n < COUNTOF(cookedvars); n++)
3353285612Sdelphij		if (!strcmp(varname, cookedvars[n].varname))
3354285612Sdelphij			return cookedvars[n].fmt;
3355285612Sdelphij
3356285612Sdelphij	return PADDING;
335754359Sroberto}
335854359Sroberto
335954359Sroberto
336054359Sroberto/*
336154359Sroberto * printvars - print variables returned in response packet
336254359Sroberto */
336354359Srobertovoid
336454359Srobertoprintvars(
3365293650Sglebius	size_t length,
3366285612Sdelphij	const char *data,
336754359Sroberto	int status,
336854359Sroberto	int sttype,
3369285612Sdelphij	int quiet,
337054359Sroberto	FILE *fp
337154359Sroberto	)
337254359Sroberto{
337354359Sroberto	if (rawmode)
3374285612Sdelphij	    rawprint(sttype, length, data, status, quiet, fp);
337554359Sroberto	else
3376285612Sdelphij	    cookedprint(sttype, length, data, status, quiet, fp);
337754359Sroberto}
337854359Sroberto
337954359Sroberto
338054359Sroberto/*
338154359Sroberto * rawprint - do a printout of the data in raw mode
338254359Sroberto */
338354359Srobertostatic void
338454359Srobertorawprint(
338554359Sroberto	int datatype,
3386293650Sglebius	size_t length,
3387285612Sdelphij	const char *data,
338854359Sroberto	int status,
3389285612Sdelphij	int quiet,
339054359Sroberto	FILE *fp
339154359Sroberto	)
339254359Sroberto{
3393285612Sdelphij	const char *cp;
3394285612Sdelphij	const char *cpend;
339554359Sroberto
339654359Sroberto	/*
339754359Sroberto	 * Essentially print the data as is.  We reformat unprintables, though.
339854359Sroberto	 */
339954359Sroberto	cp = data;
340054359Sroberto	cpend = data + length;
340154359Sroberto
3402285612Sdelphij	if (!quiet)
3403285612Sdelphij		(void) fprintf(fp, "status=0x%04x,\n", status);
340454359Sroberto
340554359Sroberto	while (cp < cpend) {
340654359Sroberto		if (*cp == '\r') {
340754359Sroberto			/*
340854359Sroberto			 * If this is a \r and the next character is a
340954359Sroberto			 * \n, supress this, else pretty print it.  Otherwise
341054359Sroberto			 * just output the character.
341154359Sroberto			 */
3412285612Sdelphij			if (cp == (cpend - 1) || *(cp + 1) != '\n')
341354359Sroberto			    makeascii(1, cp, fp);
3414330141Sdelphij		} else if (isspace(pgetc(cp)) || isprint(pgetc(cp)))
341554359Sroberto			putc(*cp, fp);
3416285612Sdelphij		else
341754359Sroberto			makeascii(1, cp, fp);
341854359Sroberto		cp++;
341954359Sroberto	}
342054359Sroberto}
342154359Sroberto
342254359Sroberto
342354359Sroberto/*
342454359Sroberto * Global data used by the cooked output routines
342554359Sroberto */
342654359Srobertoint out_chars;		/* number of characters output */
342754359Srobertoint out_linecount;	/* number of characters output on this line */
342854359Sroberto
342954359Sroberto
343054359Sroberto/*
343154359Sroberto * startoutput - get ready to do cooked output
343254359Sroberto */
343354359Srobertostatic void
343454359Srobertostartoutput(void)
343554359Sroberto{
343654359Sroberto	out_chars = 0;
343754359Sroberto	out_linecount = 0;
343854359Sroberto}
343954359Sroberto
344054359Sroberto
344154359Sroberto/*
344254359Sroberto * output - output a variable=value combination
344354359Sroberto */
344454359Srobertostatic void
344554359Srobertooutput(
344654359Sroberto	FILE *fp,
3447285612Sdelphij	const char *name,
3448285612Sdelphij	const char *value
344954359Sroberto	)
345054359Sroberto{
3451293650Sglebius	int len;
345254359Sroberto
3453285612Sdelphij	/* strlen of "name=value" */
3454293650Sglebius	len = size2int_sat(strlen(name) + 1 + strlen(value));
345554359Sroberto
345654359Sroberto	if (out_chars != 0) {
3457285612Sdelphij		out_chars += 2;
3458285612Sdelphij		if ((out_linecount + len + 2) > MAXOUTLINE) {
3459285612Sdelphij			fputs(",\n", fp);
346054359Sroberto			out_linecount = 0;
346154359Sroberto		} else {
3462285612Sdelphij			fputs(", ", fp);
3463285612Sdelphij			out_linecount += 2;
346454359Sroberto		}
346554359Sroberto	}
346654359Sroberto
346754359Sroberto	fputs(name, fp);
346854359Sroberto	putc('=', fp);
346954359Sroberto	fputs(value, fp);
3470285612Sdelphij	out_chars += len;
3471285612Sdelphij	out_linecount += len;
347254359Sroberto}
347354359Sroberto
347454359Sroberto
347554359Sroberto/*
347654359Sroberto * endoutput - terminate a block of cooked output
347754359Sroberto */
347854359Srobertostatic void
347954359Srobertoendoutput(
348054359Sroberto	FILE *fp
348154359Sroberto	)
348254359Sroberto{
348354359Sroberto	if (out_chars != 0)
3484285612Sdelphij		putc('\n', fp);
348554359Sroberto}
348654359Sroberto
348754359Sroberto
348854359Sroberto/*
348954359Sroberto * outputarr - output an array of values
349054359Sroberto */
349154359Srobertostatic void
349254359Srobertooutputarr(
349354359Sroberto	FILE *fp,
349454359Sroberto	char *name,
349554359Sroberto	int narr,
349654359Sroberto	l_fp *lfp
349754359Sroberto	)
349854359Sroberto{
3499293650Sglebius	char *bp;
3500293650Sglebius	char *cp;
3501293650Sglebius	size_t i;
3502293650Sglebius	size_t len;
350354359Sroberto	char buf[256];
350454359Sroberto
350554359Sroberto	bp = buf;
350654359Sroberto	/*
350754359Sroberto	 * Hack to align delay and offset values
350854359Sroberto	 */
350954359Sroberto	for (i = (int)strlen(name); i < 11; i++)
3510338531Sdelphij		*bp++ = ' ';
3511285612Sdelphij
351254359Sroberto	for (i = narr; i > 0; i--) {
3513338531Sdelphij		if (i != (size_t)narr)
3514338531Sdelphij			*bp++ = ' ';
351554359Sroberto		cp = lfptoms(lfp, 2);
351654359Sroberto		len = strlen(cp);
351754359Sroberto		if (len > 7) {
351854359Sroberto			cp[7] = '\0';
351954359Sroberto			len = 7;
352054359Sroberto		}
352154359Sroberto		while (len < 7) {
352254359Sroberto			*bp++ = ' ';
352354359Sroberto			len++;
352454359Sroberto		}
352554359Sroberto		while (*cp != '\0')
352654359Sroberto		    *bp++ = *cp++;
352754359Sroberto		lfp++;
352854359Sroberto	}
352954359Sroberto	*bp = '\0';
353054359Sroberto	output(fp, name, buf);
353154359Sroberto}
353254359Sroberto
353354359Srobertostatic char *
353454359Srobertotstflags(
353554359Sroberto	u_long val
353654359Sroberto	)
353754359Sroberto{
3538338531Sdelphij#	if CBLEN < 10
3539338531Sdelphij#	 error BLEN is too small -- increase!
3540338531Sdelphij#	endif
354154359Sroberto
3542338531Sdelphij	char *cp, *s;
3543338531Sdelphij	size_t cb, i;
3544338531Sdelphij	int l;
3545338531Sdelphij
3546285612Sdelphij	s = cp = circ_buf[nextcb];
354754359Sroberto	if (++nextcb >= NUMCB)
3548285612Sdelphij		nextcb = 0;
3549285612Sdelphij	cb = sizeof(circ_buf[0]);
355054359Sroberto
3551338531Sdelphij	l = snprintf(cp, cb, "%02lx", val);
3552338531Sdelphij	if (l < 0 || (size_t)l >= cb)
3553338531Sdelphij		goto fail;
3554338531Sdelphij	cp += l;
3555338531Sdelphij	cb -= l;
355654359Sroberto	if (!val) {
3557338531Sdelphij		l = strlcat(cp, " ok", cb);
3558338531Sdelphij		if ((size_t)l >= cb)
3559338531Sdelphij			goto fail;
3560338531Sdelphij		cp += l;
3561338531Sdelphij		cb -= l;
356254359Sroberto	} else {
3563338531Sdelphij		const char *sep;
3564338531Sdelphij
3565338531Sdelphij		sep = " ";
3566338531Sdelphij		for (i = 0; i < COUNTOF(tstflagnames); i++) {
356754359Sroberto			if (val & 0x1) {
3568338531Sdelphij				l = snprintf(cp, cb, "%s%s", sep,
3569338531Sdelphij					     tstflagnames[i]);
3570338531Sdelphij				if (l < 0)
3571338531Sdelphij					goto fail;
3572338531Sdelphij				if ((size_t)l >= cb) {
3573338531Sdelphij					cp += cb - 4;
3574338531Sdelphij					cb = 4;
3575338531Sdelphij					l = strlcpy (cp, "...", cb);
3576338531Sdelphij					cp += l;
3577338531Sdelphij					cb -= l;
3578338531Sdelphij					break;
3579338531Sdelphij				}
358054359Sroberto				sep = ", ";
3581338531Sdelphij				cp += l;
3582338531Sdelphij				cb -= l;
358354359Sroberto			}
358454359Sroberto			val >>= 1;
358554359Sroberto		}
358654359Sroberto	}
3587285612Sdelphij
358854359Sroberto	return s;
3589338531Sdelphij
3590338531Sdelphij  fail:
3591338531Sdelphij	*cp = '\0';
3592338531Sdelphij	return s;
359354359Sroberto}
359454359Sroberto
359554359Sroberto/*
359654359Sroberto * cookedprint - output variables in cooked mode
359754359Sroberto */
359854359Srobertostatic void
359954359Srobertocookedprint(
360054359Sroberto	int datatype,
3601293650Sglebius	size_t length,
3602285612Sdelphij	const char *data,
360354359Sroberto	int status,
3604285612Sdelphij	int quiet,
360554359Sroberto	FILE *fp
360654359Sroberto	)
360754359Sroberto{
360854359Sroberto	char *name;
360954359Sroberto	char *value;
3610132451Sroberto	char output_raw;
361154359Sroberto	int fmt;
361254359Sroberto	l_fp lfp;
3613285612Sdelphij	sockaddr_u hval;
361454359Sroberto	u_long uval;
3615285612Sdelphij	int narr;
3616285612Sdelphij	size_t len;
361754359Sroberto	l_fp lfparr[8];
3618285612Sdelphij	char b[12];
3619285612Sdelphij	char bn[2 * MAXVARLEN];
3620285612Sdelphij	char bv[2 * MAXVALLEN];
362154359Sroberto
3622285612Sdelphij	UNUSED_ARG(datatype);
362354359Sroberto
3624285612Sdelphij	if (!quiet)
3625285612Sdelphij		fprintf(fp, "status=%04x %s,\n", status,
3626285612Sdelphij			statustoa(datatype, status));
362754359Sroberto
362854359Sroberto	startoutput();
362954359Sroberto	while (nextvar(&length, &data, &name, &value)) {
3630285612Sdelphij		fmt = varfmt(name);
3631285612Sdelphij		output_raw = 0;
3632285612Sdelphij		switch (fmt) {
3633285612Sdelphij
3634285612Sdelphij		case PADDING:
363554359Sroberto			output_raw = '*';
3636285612Sdelphij			break;
363754359Sroberto
3638285612Sdelphij		case TS:
3639330141Sdelphij			if (!value || !decodets(value, &lfp))
3640285612Sdelphij				output_raw = '?';
3641285612Sdelphij			else
3642285612Sdelphij				output(fp, name, prettydate(&lfp));
3643285612Sdelphij			break;
364454359Sroberto
3645285612Sdelphij		case HA:	/* fallthru */
3646285612Sdelphij		case NA:
3647330141Sdelphij			if (!value || !decodenetnum(value, &hval)) {
3648285612Sdelphij				output_raw = '?';
3649285612Sdelphij			} else if (fmt == HA){
3650285612Sdelphij				output(fp, name, nntohost(&hval));
3651285612Sdelphij			} else {
3652285612Sdelphij				output(fp, name, stoa(&hval));
3653285612Sdelphij			}
3654285612Sdelphij			break;
365554359Sroberto
3656285612Sdelphij		case RF:
3657330141Sdelphij			if (!value) {
3658330141Sdelphij				output_raw = '?';
3659330141Sdelphij			} else if (decodenetnum(value, &hval)) {
3660285612Sdelphij				if (ISREFCLOCKADR(&hval))
3661285612Sdelphij					output(fp, name,
3662285612Sdelphij					       refnumtoa(&hval));
366354359Sroberto				else
3664285612Sdelphij					output(fp, name, stoa(&hval));
3665285612Sdelphij			} else if (strlen(value) <= 4) {
3666285612Sdelphij				output(fp, name, value);
3667285612Sdelphij			} else {
3668285612Sdelphij				output_raw = '?';
3669285612Sdelphij			}
3670285612Sdelphij			break;
367154359Sroberto
3672285612Sdelphij		case LP:
3673330141Sdelphij			if (!value || !decodeuint(value, &uval) || uval > 3) {
3674285612Sdelphij				output_raw = '?';
3675285612Sdelphij			} else {
3676285612Sdelphij				b[0] = (0x2 & uval)
3677285612Sdelphij					   ? '1'
3678285612Sdelphij					   : '0';
3679285612Sdelphij				b[1] = (0x1 & uval)
3680285612Sdelphij					   ? '1'
3681285612Sdelphij					   : '0';
3682285612Sdelphij				b[2] = '\0';
3683285612Sdelphij				output(fp, name, b);
368454359Sroberto			}
3685285612Sdelphij			break;
368654359Sroberto
3687285612Sdelphij		case OC:
3688330141Sdelphij			if (!value || !decodeuint(value, &uval)) {
3689285612Sdelphij				output_raw = '?';
3690285612Sdelphij			} else {
3691285612Sdelphij				snprintf(b, sizeof(b), "%03lo", uval);
3692285612Sdelphij				output(fp, name, b);
3693285612Sdelphij			}
3694285612Sdelphij			break;
3695285612Sdelphij
3696285612Sdelphij		case AR:
3697330141Sdelphij			if (!value || !decodearr(value, &narr, lfparr, 8))
3698285612Sdelphij				output_raw = '?';
3699285612Sdelphij			else
3700285612Sdelphij				outputarr(fp, name, narr, lfparr);
3701285612Sdelphij			break;
3702285612Sdelphij
3703285612Sdelphij		case FX:
3704330141Sdelphij			if (!value || !decodeuint(value, &uval))
3705285612Sdelphij				output_raw = '?';
3706285612Sdelphij			else
3707285612Sdelphij				output(fp, name, tstflags(uval));
3708285612Sdelphij			break;
3709285612Sdelphij
3710285612Sdelphij		default:
3711285612Sdelphij			fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3712285612Sdelphij				name, value, fmt);
3713285612Sdelphij			output_raw = '?';
3714285612Sdelphij			break;
371554359Sroberto		}
3716285612Sdelphij
371754359Sroberto		if (output_raw != 0) {
3718289997Sglebius			/* TALOS-CAN-0063: avoid buffer overrun */
3719285612Sdelphij			atoascii(name, MAXVARLEN, bn, sizeof(bn));
372054359Sroberto			if (output_raw != '*') {
3721289997Sglebius				atoascii(value, MAXVALLEN,
3722289997Sglebius					 bv, sizeof(bv) - 1);
372354359Sroberto				len = strlen(bv);
372454359Sroberto				bv[len] = output_raw;
372554359Sroberto				bv[len+1] = '\0';
3726289997Sglebius			} else {
3727289997Sglebius				atoascii(value, MAXVALLEN,
3728289997Sglebius					 bv, sizeof(bv));
372954359Sroberto			}
373054359Sroberto			output(fp, bn, bv);
373154359Sroberto		}
373254359Sroberto	}
373354359Sroberto	endoutput(fp);
373454359Sroberto}
373554359Sroberto
373654359Sroberto
373754359Sroberto/*
373854359Sroberto * sortassoc - sort associations in the cache into ascending order
373954359Sroberto */
374054359Srobertovoid
374154359Srobertosortassoc(void)
374254359Sroberto{
374354359Sroberto	if (numassoc > 1)
3744285612Sdelphij		qsort(assoc_cache, (size_t)numassoc,
3745285612Sdelphij		      sizeof(assoc_cache[0]), &assoccmp);
374654359Sroberto}
374754359Sroberto
374854359Sroberto
374954359Sroberto/*
375054359Sroberto * assoccmp - compare two associations
375154359Sroberto */
375254359Srobertostatic int
375354359Srobertoassoccmp(
375454359Sroberto	const void *t1,
375554359Sroberto	const void *t2
375654359Sroberto	)
375754359Sroberto{
3758285612Sdelphij	const struct association *ass1 = t1;
3759285612Sdelphij	const struct association *ass2 = t2;
376054359Sroberto
376154359Sroberto	if (ass1->assid < ass2->assid)
3762285612Sdelphij		return -1;
376354359Sroberto	if (ass1->assid > ass2->assid)
3764285612Sdelphij		return 1;
376554359Sroberto	return 0;
376654359Sroberto}
3767285612Sdelphij
3768285612Sdelphij
3769285612Sdelphij/*
3770285612Sdelphij * grow_assoc_cache() - enlarge dynamic assoc_cache array
3771285612Sdelphij *
3772285612Sdelphij * The strategy is to add an assumed 4k page size at a time, leaving
3773285612Sdelphij * room for malloc() bookkeeping overhead equivalent to 4 pointers.
3774285612Sdelphij */
3775285612Sdelphijvoid
3776285612Sdelphijgrow_assoc_cache(void)
3777285612Sdelphij{
3778285612Sdelphij	static size_t	prior_sz;
3779285612Sdelphij	size_t		new_sz;
3780285612Sdelphij
3781285612Sdelphij	new_sz = prior_sz + 4 * 1024;
3782285612Sdelphij	if (0 == prior_sz) {
3783285612Sdelphij		new_sz -= 4 * sizeof(void *);
3784285612Sdelphij	}
3785285612Sdelphij	assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
3786285612Sdelphij	prior_sz = new_sz;
3787293650Sglebius	assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
3788285612Sdelphij}
3789285612Sdelphij
3790285612Sdelphij
3791285612Sdelphij/*
3792285612Sdelphij * ntpq_custom_opt_handler - autoopts handler for -c and -p
3793285612Sdelphij *
3794285612Sdelphij * By default, autoopts loses the relative order of -c and -p options
3795285612Sdelphij * on the command line.  This routine replaces the default handler for
3796285612Sdelphij * those routines and builds a list of commands to execute preserving
3797285612Sdelphij * the order.
3798285612Sdelphij */
3799285612Sdelphijvoid
3800285612Sdelphijntpq_custom_opt_handler(
3801285612Sdelphij	tOptions *pOptions,
3802285612Sdelphij	tOptDesc *pOptDesc
380354359Sroberto	)
380454359Sroberto{
3805285612Sdelphij	switch (pOptDesc->optValue) {
3806285612Sdelphij
3807285612Sdelphij	default:
3808285612Sdelphij		fprintf(stderr,
3809285612Sdelphij			"ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3810285612Sdelphij			pOptDesc->optValue, pOptDesc->optValue);
3811285612Sdelphij		exit(1);
3812285612Sdelphij
3813285612Sdelphij	case 'c':
3814285612Sdelphij		ADDCMD(pOptDesc->pzLastArg);
3815285612Sdelphij		break;
3816285612Sdelphij
3817285612Sdelphij	case 'p':
3818285612Sdelphij		ADDCMD("peers");
3819285612Sdelphij		break;
3820285612Sdelphij	}
382154359Sroberto}
3822285612Sdelphij/*
3823285612Sdelphij * Obtain list of digest names
3824285612Sdelphij */
3825285612Sdelphij
3826330141Sdelphij#if defined(OPENSSL) && !defined(HAVE_EVP_MD_DO_ALL_SORTED)
3827330141Sdelphij# if defined(_MSC_VER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
3828330141Sdelphij#  define HAVE_EVP_MD_DO_ALL_SORTED
3829330141Sdelphij# endif
3830330141Sdelphij#endif
3831330141Sdelphij
3832285612Sdelphij#ifdef OPENSSL
3833285612Sdelphij# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3834330141Sdelphij#  define K_PER_LINE	8
3835330141Sdelphij#  define K_NL_PFX_STR	"\n    "
3836330141Sdelphij#  define K_DELIM_STR	", "
3837330141Sdelphij
3838285612Sdelphijstruct hstate {
3839338531Sdelphij	char *list;
3840338531Sdelphij	const char **seen;
3841338531Sdelphij	int idx;
3842285612Sdelphij};
3843330141Sdelphij
3844330141Sdelphij
3845338531Sdelphij#  ifndef BUILD_AS_LIB
3846330141Sdelphijstatic void
3847330141Sdelphijlist_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg)
3848285612Sdelphij{
3849338531Sdelphij	size_t 	       len, n;
3850338531Sdelphij	const char    *name, **seen;
3851338531Sdelphij	struct hstate *hstate = arg;
3852338531Sdelphij	const char    *cp;
3853338531Sdelphij
3854338531Sdelphij	/* m is MD obj, from is name or alias, to is base name for alias */
3855338531Sdelphij	if (!m || !from || to)
3856338531Sdelphij		return; /* Ignore aliases */
3857285612Sdelphij
3858338531Sdelphij	/* Discard MACs that NTP won't accept. */
3859338531Sdelphij	/* Keep this consistent with keytype_from_text() in ssl_init.c. */
3860338531Sdelphij	if (EVP_MD_size(m) > (MAX_MAC_LEN - sizeof(keyid_t)))
3861338531Sdelphij		return;
3862338531Sdelphij
3863338531Sdelphij	name = EVP_MD_name(m);
3864338531Sdelphij
3865338531Sdelphij	/* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
3866338531Sdelphij
3867338531Sdelphij	for (cp = name; *cp; cp++)
3868338531Sdelphij		if (islower((unsigned char)*cp))
3869338531Sdelphij			return;
3870285612Sdelphij
3871338531Sdelphij	len = (cp - name) + 1;
3872338531Sdelphij
3873338531Sdelphij	/* There are duplicates.  Discard if name has been seen. */
3874338531Sdelphij
3875338531Sdelphij	for (seen = hstate->seen; *seen; seen++)
3876338531Sdelphij		if (!strcmp(*seen, name))
3877338531Sdelphij			return;
3878330141Sdelphij
3879338531Sdelphij	n = (seen - hstate->seen) + 2;
3880338531Sdelphij	hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
3881338531Sdelphij	hstate->seen[n-2] = name;
3882338531Sdelphij	hstate->seen[n-1] = NULL;
3883338531Sdelphij
3884338531Sdelphij	if (hstate->list != NULL)
3885338531Sdelphij		len += strlen(hstate->list);
3886285612Sdelphij
3887338531Sdelphij	len += (hstate->idx >= K_PER_LINE)
3888338531Sdelphij	    ? strlen(K_NL_PFX_STR)
3889338531Sdelphij	    : strlen(K_DELIM_STR);
3890285612Sdelphij
3891338531Sdelphij	if (hstate->list == NULL) {
3892338531Sdelphij		hstate->list = (char *)emalloc(len);
3893338531Sdelphij		hstate->list[0] = '\0';
3894338531Sdelphij	} else {
3895338531Sdelphij		hstate->list = (char *)erealloc(hstate->list, len);
3896330141Sdelphij	}
3897338531Sdelphij
3898338531Sdelphij	sprintf(hstate->list + strlen(hstate->list), "%s%s",
3899338531Sdelphij		((hstate->idx >= K_PER_LINE) ? K_NL_PFX_STR : K_DELIM_STR),
3900338531Sdelphij		name);
3901338531Sdelphij
3902338531Sdelphij	if (hstate->idx >= K_PER_LINE)
3903338531Sdelphij		hstate->idx = 1;
3904338531Sdelphij	else
3905338531Sdelphij		hstate->idx++;
3906285612Sdelphij}
3907338531Sdelphij#  endif /* !defined(BUILD_AS_LIB) */
3908330141Sdelphij
3909338531Sdelphij#  ifndef BUILD_AS_LIB
3910330141Sdelphij/* Insert CMAC into SSL digests list */
3911330141Sdelphijstatic char *
3912330141Sdelphijinsert_cmac(char *list)
3913330141Sdelphij{
3914338531Sdelphij#ifdef ENABLE_CMAC
3915338531Sdelphij	int insert;
3916338531Sdelphij	size_t len;
3917330141Sdelphij
3918330141Sdelphij
3919338531Sdelphij	/* If list empty, we need to insert CMAC on new line */
3920338531Sdelphij	insert = (!list || !*list);
3921338531Sdelphij
3922330141Sdelphij	if (insert) {
3923338531Sdelphij		len = strlen(K_NL_PFX_STR) + strlen(CMAC);
3924330141Sdelphij		list = (char *)erealloc(list, len + 1);
3925338531Sdelphij		sprintf(list, "%s%s", K_NL_PFX_STR, CMAC);
3926338531Sdelphij	} else {	/* List not empty */
3927338531Sdelphij		/* Check if CMAC already in list - future proofing */
3928338531Sdelphij		const char *cmac_sn;
3929338531Sdelphij		char *cmac_p;
3930338531Sdelphij
3931338531Sdelphij		cmac_sn = OBJ_nid2sn(NID_cmac);
3932338531Sdelphij		cmac_p = list;
3933338531Sdelphij		insert = cmac_sn != NULL && *cmac_sn != '\0';
3934338531Sdelphij
3935338531Sdelphij		/* CMAC in list if found, followed by nul char or ',' */
3936338531Sdelphij		while (insert && NULL != (cmac_p = strstr(cmac_p, cmac_sn))) {
3937338531Sdelphij			cmac_p += strlen(cmac_sn);
3938338531Sdelphij			/* Still need to insert if not nul and not ',' */
3939338531Sdelphij			insert = *cmac_p && ',' != *cmac_p;
3940338531Sdelphij		}
3941338531Sdelphij
3942338531Sdelphij		/* Find proper insertion point */
3943338531Sdelphij		if (insert) {
3944338531Sdelphij			char *last_nl;
3945338531Sdelphij			char *point;
3946338531Sdelphij			char *delim;
3947338531Sdelphij			int found;
3948338531Sdelphij
3949338531Sdelphij			/* Default to start if list empty */
3950338531Sdelphij			found = 0;
3951338531Sdelphij			delim = list;
3952338531Sdelphij			len = strlen(list);
3953338531Sdelphij
3954338531Sdelphij			/* While new lines */
3955338531Sdelphij			while (delim < list + len && *delim &&
3956338531Sdelphij			       !strncmp(K_NL_PFX_STR, delim, strlen(K_NL_PFX_STR))) {
3957338531Sdelphij				point = delim + strlen(K_NL_PFX_STR);
3958338531Sdelphij
3959338531Sdelphij				/* While digest names on line */
3960338531Sdelphij				while (point < list + len && *point) {
3961338531Sdelphij					/* Another digest after on same or next line? */
3962338531Sdelphij					delim = strstr( point, K_DELIM_STR);
3963338531Sdelphij					last_nl = strstr( point, K_NL_PFX_STR);
3964338531Sdelphij
3965338531Sdelphij					/* No - end of list */
3966338531Sdelphij					if (!delim && !last_nl) {
3967338531Sdelphij						delim = list + len;
3968338531Sdelphij					} else
3969338531Sdelphij						/* New line and no delim or before delim? */
3970338531Sdelphij						if (last_nl && (!delim || last_nl < delim)) {
3971338531Sdelphij							delim = last_nl;
3972338531Sdelphij						}
3973338531Sdelphij
3974338531Sdelphij					/* Found insertion point where CMAC before entry? */
3975338531Sdelphij					if (strncmp(CMAC, point, delim - point) < 0) {
3976338531Sdelphij						found = 1;
3977338531Sdelphij						break;
3978338531Sdelphij					}
3979338531Sdelphij
3980338531Sdelphij					if (delim < list + len && *delim &&
3981338531Sdelphij					    !strncmp(K_DELIM_STR, delim, strlen(K_DELIM_STR))) {
3982338531Sdelphij						point += strlen(K_DELIM_STR);
3983338531Sdelphij					} else {
3984338531Sdelphij						break;
3985338531Sdelphij					}
3986338531Sdelphij				} /* While digest names on line */
3987338531Sdelphij			} /* While new lines */
3988338531Sdelphij
3989338531Sdelphij			/* If found in list */
3990338531Sdelphij			if (found) {
3991338531Sdelphij				/* insert cmac and delim */
3992338531Sdelphij				/* Space for list could move - save offset */
3993338531Sdelphij				ptrdiff_t p_offset = point - list;
3994338531Sdelphij				len += strlen(CMAC) + strlen(K_DELIM_STR);
3995338531Sdelphij				list = (char *)erealloc(list, len + 1);
3996338531Sdelphij				point = list + p_offset;
3997338531Sdelphij				/* move to handle src/dest overlap */
3998338531Sdelphij				memmove(point + strlen(CMAC) + strlen(K_DELIM_STR),
3999330141Sdelphij					point, strlen(point) + 1);
4000338531Sdelphij				strncpy(point, CMAC, strlen(CMAC));
4001338531Sdelphij				strncpy(point + strlen(CMAC), K_DELIM_STR, strlen(K_DELIM_STR));
4002338531Sdelphij			} else {	/* End of list */
4003338531Sdelphij				/* append delim and cmac */
4004338531Sdelphij				len += strlen(K_DELIM_STR) + strlen(CMAC);
4005338531Sdelphij				list = (char *)erealloc(list, len + 1);
4006338531Sdelphij				strcpy(list + strlen(list), K_DELIM_STR);
4007338531Sdelphij				strcpy(list + strlen(list), CMAC);
4008338531Sdelphij			}
4009338531Sdelphij		} /* insert */
4010338531Sdelphij	} /* List not empty */
4011338531Sdelphij#endif /*ENABLE_CMAC*/
4012338531Sdelphij	return list;
4013330141Sdelphij}
4014338531Sdelphij#  endif /* !defined(BUILD_AS_LIB) */
4015285612Sdelphij# endif
4016285612Sdelphij#endif
4017285612Sdelphij
4018330141Sdelphij
4019338531Sdelphij#ifndef BUILD_AS_LIB
4020330141Sdelphijstatic char *
4021330141Sdelphijlist_digest_names(void)
4022285612Sdelphij{
4023338531Sdelphij	char *list = NULL;
4024338531Sdelphij
4025285612Sdelphij#ifdef OPENSSL
4026285612Sdelphij# ifdef HAVE_EVP_MD_DO_ALL_SORTED
4027338531Sdelphij	struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
4028338531Sdelphij
4029338531Sdelphij	/* replace calloc(1, sizeof(const char *)) */
4030338531Sdelphij	hstate.seen = (const char **)emalloc_zero(sizeof(const char *));
4031338531Sdelphij
4032338531Sdelphij	INIT_SSL();
4033338531Sdelphij	EVP_MD_do_all_sorted(list_md_fn, &hstate);
4034338531Sdelphij	list = hstate.list;
4035338531Sdelphij	free(hstate.seen);
4036338531Sdelphij
4037338531Sdelphij	list = insert_cmac(list);	/* Insert CMAC into SSL digests list */
4038338531Sdelphij
4039285612Sdelphij# else
4040338531Sdelphij	list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
4041338531Sdelphij	strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
4042285612Sdelphij# endif
4043285612Sdelphij#else
4044338531Sdelphij	list = (char *)emalloc(sizeof("md5"));
4045338531Sdelphij	strcpy(list, "md5");
4046285612Sdelphij#endif
4047338531Sdelphij
4048338531Sdelphij	return list;
4049285612Sdelphij}
4050338531Sdelphij#endif /* !defined(BUILD_AS_LIB) */
4051293650Sglebius
4052293650Sglebius#define CTRLC_STACK_MAX 4
4053293650Sglebiusstatic volatile size_t		ctrlc_stack_len = 0;
4054293650Sglebiusstatic volatile Ctrl_C_Handler	ctrlc_stack[CTRLC_STACK_MAX];
4055293650Sglebius
4056293650Sglebius
4057293650Sglebius
4058293650Sglebiusint/*BOOL*/
4059293650Sglebiuspush_ctrl_c_handler(
4060293650Sglebius	Ctrl_C_Handler func
4061293650Sglebius	)
4062293650Sglebius{
4063293650Sglebius	size_t size = ctrlc_stack_len;
4064293650Sglebius	if (func && (size < CTRLC_STACK_MAX)) {
4065293650Sglebius		ctrlc_stack[size] = func;
4066293650Sglebius		ctrlc_stack_len = size + 1;
4067293650Sglebius		return TRUE;
4068293650Sglebius	}
4069293650Sglebius	return FALSE;
4070293650Sglebius}
4071293650Sglebius
4072293650Sglebiusint/*BOOL*/
4073293650Sglebiuspop_ctrl_c_handler(
4074293650Sglebius	Ctrl_C_Handler func
4075293650Sglebius	)
4076293650Sglebius{
4077293650Sglebius	size_t size = ctrlc_stack_len;
4078293650Sglebius	if (size) {
4079293650Sglebius		--size;
4080293650Sglebius		if (func == NULL || func == ctrlc_stack[size]) {
4081293650Sglebius			ctrlc_stack_len = size;
4082293650Sglebius			return TRUE;
4083293650Sglebius		}
4084293650Sglebius	}
4085293650Sglebius	return FALSE;
4086293650Sglebius}
4087293650Sglebius
4088338531Sdelphij#ifndef BUILD_AS_LIB
4089293650Sglebiusstatic void
4090293650Sglebiuson_ctrlc(void)
4091293650Sglebius{
4092293650Sglebius	size_t size = ctrlc_stack_len;
4093293650Sglebius	while (size)
4094293650Sglebius		if ((*ctrlc_stack[--size])())
4095293650Sglebius			break;
4096293650Sglebius}
4097338531Sdelphij#endif /* !defined(BUILD_AS_LIB) */
4098294569Sdelphij
4099338531Sdelphij#ifndef BUILD_AS_LIB
4100294569Sdelphijstatic int
4101294569Sdelphijmy_easprintf(
4102294569Sdelphij	char ** 	ppinto,
4103294569Sdelphij	const char *	fmt   ,
4104294569Sdelphij	...
4105294569Sdelphij	)
4106294569Sdelphij{
4107294569Sdelphij	va_list	va;
4108294569Sdelphij	int	prc;
4109294569Sdelphij	size_t	len = 128;
4110294569Sdelphij	char *	buf = emalloc(len);
4111294569Sdelphij
4112294569Sdelphij  again:
4113294569Sdelphij	/* Note: we expect the memory allocation to fail long before the
4114294569Sdelphij	 * increment in buffer size actually overflows.
4115294569Sdelphij	 */
4116294569Sdelphij	buf = (buf) ? erealloc(buf, len) : emalloc(len);
4117294569Sdelphij
4118294569Sdelphij	va_start(va, fmt);
4119294569Sdelphij	prc = vsnprintf(buf, len, fmt, va);
4120294569Sdelphij	va_end(va);
4121294569Sdelphij
4122294569Sdelphij	if (prc < 0) {
4123294569Sdelphij		/* might be very old vsnprintf. Or actually MSVC... */
4124294569Sdelphij		len += len >> 1;
4125294569Sdelphij		goto again;
4126294569Sdelphij	}
4127294569Sdelphij	if ((size_t)prc >= len) {
4128294569Sdelphij		/* at least we have the proper size now... */
4129294569Sdelphij		len = (size_t)prc + 1;
4130294569Sdelphij		goto again;
4131294569Sdelphij	}
4132294569Sdelphij	if ((size_t)prc < (len - 32))
4133294569Sdelphij		buf = erealloc(buf, (size_t)prc + 1);
4134294569Sdelphij	*ppinto = buf;
4135294569Sdelphij	return prc;
4136294569Sdelphij}
4137338531Sdelphij#endif /* !defined(BUILD_AS_LIB) */
4138