154359Sroberto/*
254359Sroberto * ntpdc - control and monitor your ntpd daemon
354359Sroberto */
4290000Sglebius#include <config.h>
554359Sroberto#include <stdio.h>
6290000Sglebius#include <stddef.h>
7182007Sroberto#include <ctype.h>
8182007Sroberto#include <signal.h>
9182007Sroberto#include <setjmp.h>
10290000Sglebius#ifdef HAVE_UNISTD_H
11290000Sglebius# include <unistd.h>
12290000Sglebius#endif
13290000Sglebius#ifdef HAVE_FCNTL_H
14290000Sglebius# include <fcntl.h>
15290000Sglebius#endif
16290000Sglebius#ifdef SYS_WINNT
17290000Sglebius# include <mswsock.h>
18290000Sglebius#endif
19290000Sglebius#include <isc/net.h>
20290000Sglebius#include <isc/result.h>
21182007Sroberto
2282498Sroberto#include "ntpdc.h"
2382498Sroberto#include "ntp_select.h"
2482498Sroberto#include "ntp_stdlib.h"
25290000Sglebius#include "ntp_assert.h"
26290000Sglebius#include "ntp_lineedit.h"
27290000Sglebius#ifdef OPENSSL
28290000Sglebius#include "openssl/evp.h"
29290000Sglebius#include "openssl/objects.h"
30290000Sglebius#endif
31290000Sglebius#include <ssl_applink.c>
3282498Sroberto
33290000Sglebius#include "ntp_libopts.h"
34182007Sroberto#include "ntpdc-opts.h"
35293894Sglebius#include "safecast.h"
3654359Sroberto
3754359Sroberto#ifdef SYS_VXWORKS
38182007Sroberto				/* vxWorks needs mode flag -casey*/
39182007Sroberto# define open(name, flags)   open(name, flags, 0777)
40182007Sroberto# define SERVER_PORT_NUM     123
4154359Sroberto#endif
4254359Sroberto
43182007Sroberto/* We use COMMAND as an autogen keyword */
44182007Sroberto#ifdef COMMAND
45182007Sroberto# undef COMMAND
46182007Sroberto#endif
47182007Sroberto
4854359Sroberto/*
4954359Sroberto * Because we now potentially understand a lot of commands (and
5054359Sroberto * it requires a lot of commands to talk to ntpd) we will run
5154359Sroberto * interactive if connected to a terminal.
5254359Sroberto */
5354359Srobertostatic	int	interactive = 0;	/* set to 1 when we should prompt */
5454359Srobertostatic	const char *	prompt = "ntpdc> ";	/* prompt to ask him about */
5554359Sroberto
5654359Sroberto/*
5754359Sroberto * Keyid used for authenticated requests.  Obtained on the fly.
5854359Sroberto */
5954359Srobertostatic	u_long	info_auth_keyid;
60182007Srobertostatic int keyid_entered = 0;
6154359Sroberto
62290000Sglebiusstatic	int	info_auth_keytype = NID_md5;	/* MD5 */
63290000Sglebiusstatic	size_t	info_auth_hashlen = 16;		/* MD5 */
6454359Srobertou_long	current_time;		/* needed by authkeys; not used */
6554359Sroberto
66182007Sroberto/*
67182007Sroberto * for get_systime()
68182007Sroberto */
69182007Srobertos_char	sys_precision;		/* local clock precision (log2 s) */
70182007Sroberto
71290000Sglebiusint		ntpdcmain	(int,	char **);
7254359Sroberto/*
7354359Sroberto * Built in command handler declarations
7454359Sroberto */
75290000Sglebiusstatic	int	openhost	(const char *);
76290000Sglebiusstatic	int	sendpkt		(void *, size_t);
77290000Sglebiusstatic	void	growpktdata	(void);
78293894Sglebiusstatic	int	getresponse	(int, int, size_t *, size_t *, const char **, size_t);
79293894Sglebiusstatic	int	sendrequest	(int, int, int, size_t, size_t, const char *);
80290000Sglebiusstatic	void	getcmds		(void);
81290000Sglebiusstatic	RETSIGTYPE abortcmd	(int);
82290000Sglebiusstatic	void	docmd		(const char *);
83290000Sglebiusstatic	void	tokenize	(const char *, char **, int *);
84290000Sglebiusstatic	int	findcmd		(char *, struct xcmd *, struct xcmd *, struct xcmd **);
85290000Sglebiusstatic	int	getarg		(char *, int, arg_v *);
86290000Sglebiusstatic	int	getnetnum	(const char *, sockaddr_u *, char *, int);
87290000Sglebiusstatic	void	help		(struct parse *, FILE *);
88290000Sglebiusstatic	int	helpsort	(const void *, const void *);
89290000Sglebiusstatic	void	printusage	(struct xcmd *, FILE *);
90290000Sglebiusstatic	void	timeout		(struct parse *, FILE *);
91290000Sglebiusstatic	void	my_delay	(struct parse *, FILE *);
92290000Sglebiusstatic	void	host		(struct parse *, FILE *);
93290000Sglebiusstatic	void	keyid		(struct parse *, FILE *);
94290000Sglebiusstatic	void	keytype		(struct parse *, FILE *);
95290000Sglebiusstatic	void	passwd		(struct parse *, FILE *);
96290000Sglebiusstatic	void	hostnames	(struct parse *, FILE *);
97290000Sglebiusstatic	void	setdebug	(struct parse *, FILE *);
98290000Sglebiusstatic	void	quit		(struct parse *, FILE *);
99290000Sglebiusstatic	void	version		(struct parse *, FILE *);
100290000Sglebiusstatic	void	warning		(const char *, ...)
101290000Sglebius    __attribute__((__format__(__printf__, 1, 2)));
102290000Sglebiusstatic	void	error		(const char *, ...)
103290000Sglebius    __attribute__((__format__(__printf__, 1, 2)));
104290000Sglebiusstatic	u_long	getkeyid	(const char *);
10554359Sroberto
10654359Sroberto
10754359Sroberto
10854359Sroberto/*
10954359Sroberto * Built-in commands we understand
11054359Sroberto */
11154359Srobertostatic	struct xcmd builtins[] = {
11254359Sroberto	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
11354359Sroberto	  { "command", "", "", "" },
11454359Sroberto	  "tell the use and syntax of commands" },
11554359Sroberto	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
11654359Sroberto	  { "command", "", "", "" },
11754359Sroberto	  "tell the use and syntax of commands" },
118182007Sroberto	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
11954359Sroberto	  { "msec", "", "", "" },
12054359Sroberto	  "set the primary receive time out" },
121182007Sroberto	{ "delay",	my_delay,	{ OPT|NTP_INT, NO, NO, NO },
12254359Sroberto	  { "msec", "", "", "" },
12354359Sroberto	  "set the delay added to encryption time stamps" },
124132451Sroberto	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
125132451Sroberto	  { "-4|-6", "hostname", "", "" },
12654359Sroberto	  "specify the host whose NTP server we talk to" },
12754359Sroberto	{ "passwd",	passwd,		{ OPT|NTP_STR, NO, NO, NO },
12854359Sroberto	  { "", "", "", "" },
12954359Sroberto	  "specify a password to use for authenticated requests"},
13054359Sroberto	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
13154359Sroberto	  { "yes|no", "", "", "" },
13254359Sroberto	  "specify whether hostnames or net numbers are printed"},
13354359Sroberto	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
13454359Sroberto	  { "no|more|less", "", "", "" },
13554359Sroberto	  "set/change debugging level" },
13654359Sroberto	{ "quit",	quit,		{ NO, NO, NO, NO },
13754359Sroberto	  { "", "", "", "" },
13854359Sroberto	  "exit ntpdc" },
13954359Sroberto	{ "exit",	quit,		{ NO, NO, NO, NO },
14054359Sroberto	  { "", "", "", "" },
14154359Sroberto	  "exit ntpdc" },
142182007Sroberto	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
14354359Sroberto	  { "key#", "", "", "" },
14454359Sroberto	  "set/show keyid to use for authenticated requests" },
14554359Sroberto	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
14654359Sroberto	  { "(md5|des)", "", "", "" },
14754359Sroberto	  "set/show key authentication type for authenticated requests (des|md5)" },
14854359Sroberto	{ "version",	version,	{ NO, NO, NO, NO },
14954359Sroberto	  { "", "", "", "" },
15054359Sroberto	  "print version number" },
15154359Sroberto	{ 0,		0,		{ NO, NO, NO, NO },
15254359Sroberto	  { "", "", "", "" }, "" }
15354359Sroberto};
15454359Sroberto
15554359Sroberto
15654359Sroberto/*
15754359Sroberto * Default values we use.
15854359Sroberto */
159290000Sglebius#define	DEFHOST		"localhost"	/* default host name */
16054359Sroberto#define	DEFTIMEOUT	(5)		/* 5 second time out */
16154359Sroberto#define	DEFSTIMEOUT	(2)		/* 2 second time out after first */
16254359Sroberto#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
16354359Sroberto#define	LENHOSTNAME	256		/* host name is 256 characters long */
16454359Sroberto#define	MAXCMDS		100		/* maximum commands on cmd line */
16554359Sroberto#define	MAXHOSTS	200		/* maximum hosts on cmd line */
16654359Sroberto#define	MAXLINE		512		/* maximum line length */
167182007Sroberto#define	MAXTOKENS	(1+1+MAXARGS+MOREARGS+2)	/* maximum number of usable tokens */
168182007Sroberto#define	SCREENWIDTH  	78		/* nominal screen width in columns */
16954359Sroberto
17054359Sroberto/*
17154359Sroberto * Some variables used and manipulated locally
17254359Sroberto */
173290000Sglebiusstatic	struct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
174290000Sglebiusstatic	struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
17554359Srobertostatic	l_fp delay_time;				/* delay time */
17654359Srobertostatic	char currenthost[LENHOSTNAME];			/* current host name */
177132451Srobertoint showhostnames = 1;					/* show host names by default */
17854359Sroberto
179132451Srobertostatic	int ai_fam_templ;				/* address family */
180132451Srobertostatic	int ai_fam_default;				/* default address family */
181132451Srobertostatic	SOCKET sockfd;					/* fd socket is opened on */
18254359Srobertostatic	int havehost = 0;				/* set to 1 when host open */
183132451Srobertoint s_port = 0;
18454359Sroberto
18554359Sroberto/*
18654359Sroberto * Holds data returned from queries.  We allocate INITDATASIZE
18754359Sroberto * octets to begin with, increasing this as we need to.
18854359Sroberto */
18954359Sroberto#define	INITDATASIZE	(sizeof(struct resp_pkt) * 16)
19054359Sroberto#define	INCDATASIZE	(sizeof(struct resp_pkt) * 8)
19154359Sroberto
19254359Srobertostatic	char *pktdata;
19354359Srobertostatic	int pktdatasize;
19454359Sroberto
19554359Sroberto/*
196106163Sroberto * These are used to help the magic with old and new versions of ntpd.
197106163Sroberto */
198132451Srobertoint impl_ver = IMPL_XNTPD;
199106163Srobertostatic int req_pkt_size = REQ_LEN_NOMAC;
200106163Sroberto
201106163Sroberto/*
20254359Sroberto * For commands typed on the command line (with the -c option)
20354359Sroberto */
20454359Srobertostatic	int numcmds = 0;
20554359Srobertostatic	const char *ccmds[MAXCMDS];
20654359Sroberto#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
20754359Sroberto
20854359Sroberto/*
20954359Sroberto * When multiple hosts are specified.
21054359Sroberto */
21154359Srobertostatic	int numhosts = 0;
21254359Srobertostatic	const char *chosts[MAXHOSTS];
21354359Sroberto#define	ADDHOST(cp)	if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
21454359Sroberto
21554359Sroberto/*
21654359Sroberto * Error codes for internal use
21754359Sroberto */
21854359Sroberto#define	ERR_INCOMPLETE		16
21954359Sroberto#define	ERR_TIMEOUT		17
22054359Sroberto
22154359Sroberto/*
22254359Sroberto * Macro definitions we use
22354359Sroberto */
22454359Sroberto#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
22554359Sroberto#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
22654359Sroberto#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
22754359Sroberto
22854359Sroberto/*
22954359Sroberto * Jump buffer for longjumping back to the command level
23054359Sroberto */
23154359Srobertostatic	jmp_buf interrupt_buf;
23254359Srobertostatic  volatile int jump = 0;
23354359Sroberto
23454359Sroberto/*
23554359Sroberto * Pointer to current output unit
23654359Sroberto */
23754359Srobertostatic	FILE *current_output;
23854359Sroberto
23954359Sroberto/*
24054359Sroberto * Command table imported from ntpdc_ops.c
24154359Sroberto */
24254359Srobertoextern struct xcmd opcmds[];
24354359Sroberto
244290000Sglebiuschar const *progname;
24554359Sroberto
24654359Sroberto#ifdef NO_MAIN_ALLOWED
24754359SrobertoCALL(ntpdc,"ntpdc",ntpdcmain);
24854359Sroberto#else
24954359Srobertoint
25054359Srobertomain(
25154359Sroberto	int argc,
25254359Sroberto	char *argv[]
25354359Sroberto	)
25454359Sroberto{
25554359Sroberto	return ntpdcmain(argc, argv);
25654359Sroberto}
25754359Sroberto#endif
25854359Sroberto
25954359Sroberto#ifdef SYS_VXWORKS
26054359Srobertovoid clear_globals(void)
26154359Sroberto{
26254359Sroberto    showhostnames = 0;              /* show host names by default */
26354359Sroberto    havehost = 0;                   /* set to 1 when host open */
26454359Sroberto    numcmds = 0;
26554359Sroberto    numhosts = 0;
26654359Sroberto}
26754359Sroberto#endif
26854359Sroberto
26954359Sroberto/*
27054359Sroberto * main - parse arguments and handle options
27154359Sroberto */
27254359Srobertoint
27354359Srobertontpdcmain(
27454359Sroberto	int argc,
27554359Sroberto	char *argv[]
27654359Sroberto	)
27754359Sroberto{
27854359Sroberto
27954359Sroberto	delay_time.l_ui = 0;
28054359Sroberto	delay_time.l_uf = DEFDELAY;
28154359Sroberto
28254359Sroberto#ifdef SYS_VXWORKS
28354359Sroberto	clear_globals();
28454359Sroberto	taskPrioritySet(taskIdSelf(), 100 );
28554359Sroberto#endif
28654359Sroberto
287290000Sglebius	init_lib();	/* sets up ipv4_works, ipv6_works */
288290000Sglebius	ssl_applink();
289290000Sglebius	init_auth();
290132451Sroberto
291290000Sglebius	/* Check to see if we have IPv6. Otherwise default to IPv4 */
292290000Sglebius	if (!ipv6_works)
293132451Sroberto		ai_fam_default = AF_INET;
294132451Sroberto
29554359Sroberto	progname = argv[0];
296182007Sroberto
297182007Sroberto	{
298290000Sglebius		int optct = ntpOptionProcess(&ntpdcOptions, argc, argv);
299182007Sroberto		argc -= optct;
300182007Sroberto		argv += optct;
301182007Sroberto	}
302182007Sroberto
303290000Sglebius	if (HAVE_OPT(IPV4))
304182007Sroberto		ai_fam_templ = AF_INET;
305290000Sglebius	else if (HAVE_OPT(IPV6))
306182007Sroberto		ai_fam_templ = AF_INET6;
307290000Sglebius	else
308182007Sroberto		ai_fam_templ = ai_fam_default;
309182007Sroberto
310182007Sroberto	if (HAVE_OPT(COMMAND)) {
311182007Sroberto		int		cmdct = STACKCT_OPT( COMMAND );
312182007Sroberto		const char**	cmds  = STACKLST_OPT( COMMAND );
313182007Sroberto
314182007Sroberto		while (cmdct-- > 0) {
315182007Sroberto			ADDCMD(*cmds++);
316182007Sroberto		}
317182007Sroberto	}
318182007Sroberto
319290000Sglebius	debug = OPT_VALUE_SET_DEBUG_LEVEL;
320182007Sroberto
321182007Sroberto	if (HAVE_OPT(INTERACTIVE)) {
322182007Sroberto		interactive = 1;
323182007Sroberto	}
324182007Sroberto
325182007Sroberto	if (HAVE_OPT(NUMERIC)) {
326182007Sroberto		showhostnames = 0;
327182007Sroberto	}
328182007Sroberto
329182007Sroberto	if (HAVE_OPT(LISTPEERS)) {
330182007Sroberto		ADDCMD("listpeers");
331182007Sroberto	}
332182007Sroberto
333182007Sroberto	if (HAVE_OPT(PEERS)) {
334182007Sroberto		ADDCMD("peers");
335182007Sroberto	}
336182007Sroberto
337182007Sroberto	if (HAVE_OPT(SHOWPEERS)) {
338182007Sroberto		ADDCMD("dmpeers");
339182007Sroberto	}
340182007Sroberto
341182007Sroberto	if (ntp_optind == argc) {
342182007Sroberto		ADDHOST(DEFHOST);
343182007Sroberto	} else {
344182007Sroberto		for (; ntp_optind < argc; ntp_optind++)
345182007Sroberto		    ADDHOST(argv[ntp_optind]);
346182007Sroberto	}
347182007Sroberto
348182007Sroberto	if (numcmds == 0 && interactive == 0
349182007Sroberto	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
350182007Sroberto		interactive = 1;
351182007Sroberto	}
352182007Sroberto
35354359Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
35454359Sroberto	if (interactive)
35554359Sroberto	    (void) signal_no_reset(SIGINT, abortcmd);
35654359Sroberto#endif /* SYS_WINNT */
35754359Sroberto
35854359Sroberto	/*
35954359Sroberto	 * Initialize the packet data buffer
36054359Sroberto	 */
36154359Sroberto	pktdatasize = INITDATASIZE;
362290000Sglebius	pktdata = emalloc(INITDATASIZE);
36354359Sroberto
36454359Sroberto	if (numcmds == 0) {
36554359Sroberto		(void) openhost(chosts[0]);
36654359Sroberto		getcmds();
36754359Sroberto	} else {
36854359Sroberto		int ihost;
36954359Sroberto		int icmd;
37054359Sroberto
37154359Sroberto		for (ihost = 0; ihost < numhosts; ihost++) {
37254359Sroberto			if (openhost(chosts[ihost]))
37354359Sroberto			    for (icmd = 0; icmd < numcmds; icmd++) {
37454359Sroberto				    if (numhosts > 1)
37554359Sroberto					printf ("--- %s ---\n",chosts[ihost]);
37654359Sroberto				    docmd(ccmds[icmd]);
37754359Sroberto			    }
37854359Sroberto		}
37954359Sroberto	}
38054359Sroberto#ifdef SYS_WINNT
38154359Sroberto	WSACleanup();
38254359Sroberto#endif
38354359Sroberto	return(0);
38454359Sroberto} /* main end */
38554359Sroberto
38654359Sroberto
38754359Sroberto/*
38854359Sroberto * openhost - open a socket to a host
38954359Sroberto */
39054359Srobertostatic int
39154359Srobertoopenhost(
39254359Sroberto	const char *hname
39354359Sroberto	)
39454359Sroberto{
39554359Sroberto	char temphost[LENHOSTNAME];
396132451Sroberto	int a_info, i;
397132451Sroberto	struct addrinfo hints, *ai = NULL;
398290000Sglebius	sockaddr_u addr;
399290000Sglebius	size_t octets;
400132451Sroberto	register const char *cp;
401132451Sroberto	char name[LENHOSTNAME];
402132451Sroberto	char service[5];
40354359Sroberto
404132451Sroberto	/*
405132451Sroberto	 * We need to get by the [] if they were entered
406132451Sroberto	 */
407132451Sroberto
408132451Sroberto	cp = hname;
409132451Sroberto
410132451Sroberto	if (*cp == '[') {
411132451Sroberto		cp++;
412290000Sglebius		for (i = 0; *cp && *cp != ']'; cp++, i++)
413290000Sglebius			name[i] = *cp;
414290000Sglebius		if (*cp == ']') {
415290000Sglebius			name[i] = '\0';
416290000Sglebius			hname = name;
417290000Sglebius		} else {
418290000Sglebius			return 0;
419290000Sglebius		}
420132451Sroberto	}
421132451Sroberto
422132451Sroberto	/*
423132451Sroberto	 * First try to resolve it as an ip address and if that fails,
424132451Sroberto	 * do a fullblown (dns) lookup. That way we only use the dns
425132451Sroberto	 * when it is needed and work around some implementations that
426132451Sroberto	 * will return an "IPv4-mapped IPv6 address" address if you
427132451Sroberto	 * give it an IPv4 address to lookup.
428132451Sroberto	 */
429290000Sglebius	strlcpy(service, "ntp", sizeof(service));
430290000Sglebius	ZERO(hints);
431132451Sroberto	hints.ai_family = ai_fam_templ;
432132451Sroberto	hints.ai_protocol = IPPROTO_UDP;
433132451Sroberto	hints.ai_socktype = SOCK_DGRAM;
434290000Sglebius	hints.ai_flags = Z_AI_NUMERICHOST;
435132451Sroberto
436132451Sroberto	a_info = getaddrinfo(hname, service, &hints, &ai);
437182007Sroberto	if (a_info == EAI_NONAME
438182007Sroberto#ifdef EAI_NODATA
439182007Sroberto	    || a_info == EAI_NODATA
440182007Sroberto#endif
441182007Sroberto	   ) {
442132451Sroberto		hints.ai_flags = AI_CANONNAME;
443132451Sroberto#ifdef AI_ADDRCONFIG
444132451Sroberto		hints.ai_flags |= AI_ADDRCONFIG;
445132451Sroberto#endif
446132451Sroberto		a_info = getaddrinfo(hname, service, &hints, &ai);
44754359Sroberto	}
448132451Sroberto	/* Some older implementations don't like AI_ADDRCONFIG. */
449132451Sroberto	if (a_info == EAI_BADFLAGS) {
450132451Sroberto		hints.ai_flags = AI_CANONNAME;
451132451Sroberto		a_info = getaddrinfo(hname, service, &hints, &ai);
452132451Sroberto	}
453132451Sroberto	if (a_info != 0) {
454290000Sglebius		fprintf(stderr, "%s\n", gai_strerror(a_info));
455182007Sroberto		if (ai != NULL)
456182007Sroberto			freeaddrinfo(ai);
457132451Sroberto		return 0;
458132451Sroberto	}
45954359Sroberto
460290000Sglebius	/*
461290000Sglebius	 * getaddrinfo() has returned without error so ai should not
462290000Sglebius	 * be NULL.
463290000Sglebius	 */
464290000Sglebius	INSIST(ai != NULL);
465290000Sglebius	ZERO(addr);
466290000Sglebius	octets = min(sizeof(addr), ai->ai_addrlen);
467290000Sglebius	memcpy(&addr, ai->ai_addr, octets);
468132451Sroberto
469290000Sglebius	if (ai->ai_canonname == NULL)
470290000Sglebius		strlcpy(temphost, stoa(&addr), sizeof(temphost));
471290000Sglebius	else
472290000Sglebius		strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
473290000Sglebius
47454359Sroberto	if (debug > 2)
475290000Sglebius		printf("Opening host %s\n", temphost);
47654359Sroberto
47754359Sroberto	if (havehost == 1) {
47854359Sroberto		if (debug > 2)
479290000Sglebius			printf("Closing old host %s\n", currenthost);
480290000Sglebius		closesocket(sockfd);
48154359Sroberto		havehost = 0;
48254359Sroberto	}
483290000Sglebius	strlcpy(currenthost, temphost, sizeof(currenthost));
484132451Sroberto
485132451Sroberto	/* port maps to the same in both families */
486290000Sglebius	s_port = NSRCPORT(&addr);;
487132451Sroberto#ifdef SYS_VXWORKS
488132451Sroberto	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
489132451Sroberto	if (ai->ai_family == AF_INET)
490132451Sroberto		*(struct sockaddr_in *)&hostaddr=
491132451Sroberto			*((struct sockaddr_in *)ai->ai_addr);
492132451Sroberto	else
493132451Sroberto		*(struct sockaddr_in6 *)&hostaddr=
494132451Sroberto			*((struct sockaddr_in6 *)ai->ai_addr);
495132451Sroberto#endif /* SYS_VXWORKS */
49654359Sroberto
49754359Sroberto#ifdef SYS_WINNT
49854359Sroberto	{
49954359Sroberto		int optionValue = SO_SYNCHRONOUS_NONALERT;
50054359Sroberto		int err;
501182007Sroberto
50254359Sroberto		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue));
50354359Sroberto		if (err != NO_ERROR) {
50454359Sroberto			(void) fprintf(stderr, "cannot open nonoverlapped sockets\n");
50554359Sroberto			exit(1);
50654359Sroberto		}
50754359Sroberto	}
508290000Sglebius#endif /* SYS_WINNT */
509132451Sroberto
510132451Sroberto	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
51154359Sroberto	if (sockfd == INVALID_SOCKET) {
512290000Sglebius		error("socket");
51354359Sroberto		exit(-1);
51454359Sroberto	}
51554359Sroberto
51654359Sroberto#ifdef NEED_RCVBUF_SLOP
51754359Sroberto# ifdef SO_RCVBUF
51854359Sroberto	{
51954359Sroberto		int rbufsize = INITDATASIZE + 2048; /* 2K for slop */
52054359Sroberto
52154359Sroberto		if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
52254359Sroberto			       &rbufsize, sizeof(int)) == -1)
523290000Sglebius		    error("setsockopt");
52454359Sroberto	}
52554359Sroberto# endif
52654359Sroberto#endif
52754359Sroberto
528132451Sroberto#ifdef SYS_VXWORKS
529132451Sroberto	if (connect(sockfd, (struct sockaddr *)&hostaddr,
530293894Sglebius		    sizeof(hostaddr)) == -1)
531132451Sroberto#else
532293894Sglebius	if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) == -1)
533132451Sroberto#endif /* SYS_VXWORKS */
534293894Sglebius	{
535290000Sglebius		error("connect");
536290000Sglebius		exit(-1);
537290000Sglebius	}
538290000Sglebius
539290000Sglebius	freeaddrinfo(ai);
54054359Sroberto	havehost = 1;
541106163Sroberto	req_pkt_size = REQ_LEN_NOMAC;
542132451Sroberto	impl_ver = IMPL_XNTPD;
54354359Sroberto	return 1;
54454359Sroberto}
54554359Sroberto
54654359Sroberto
54754359Sroberto/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
54854359Sroberto/*
54954359Sroberto * sendpkt - send a packet to the remote host
55054359Sroberto */
55154359Srobertostatic int
55254359Srobertosendpkt(
553290000Sglebius	void *	xdata,
554290000Sglebius	size_t	xdatalen
55554359Sroberto	)
55654359Sroberto{
557290000Sglebius	if (send(sockfd, xdata, xdatalen, 0) == -1) {
558290000Sglebius		warning("write to %s failed", currenthost);
55954359Sroberto		return -1;
56054359Sroberto	}
56154359Sroberto
56254359Sroberto	return 0;
56354359Sroberto}
56454359Sroberto
56554359Sroberto
56654359Sroberto/*
56754359Sroberto * growpktdata - grow the packet data area
56854359Sroberto */
56954359Srobertostatic void
57054359Srobertogrowpktdata(void)
57154359Sroberto{
572290000Sglebius	size_t priorsz;
573290000Sglebius
574290000Sglebius	priorsz = (size_t)pktdatasize;
57554359Sroberto	pktdatasize += INCDATASIZE;
576290000Sglebius	pktdata = erealloc_zero(pktdata, (size_t)pktdatasize, priorsz);
57754359Sroberto}
57854359Sroberto
57954359Sroberto
58054359Sroberto/*
58154359Sroberto * getresponse - get a (series of) response packet(s) and return the data
58254359Sroberto */
58354359Srobertostatic int
58454359Srobertogetresponse(
58554359Sroberto	int implcode,
58654359Sroberto	int reqcode,
587293894Sglebius	size_t *ritems,
588293894Sglebius	size_t *rsize,
589293894Sglebius	const char **rdata,
590293894Sglebius	size_t esize
59154359Sroberto	)
59254359Sroberto{
59354359Sroberto	struct resp_pkt rpkt;
594290000Sglebius	struct sock_timeval tvo;
595293894Sglebius	size_t items;
596293894Sglebius	size_t i;
597293894Sglebius	size_t size;
598293894Sglebius	size_t datasize;
59954359Sroberto	char *datap;
600132451Sroberto	char *tmp_data;
60154359Sroberto	char haveseq[MAXSEQ+1];
60254359Sroberto	int firstpkt;
60354359Sroberto	int lastseq;
60454359Sroberto	int numrecv;
60554359Sroberto	int seq;
60654359Sroberto	fd_set fds;
607290000Sglebius	ssize_t n;
608294904Sdelphij	int pad;
609294904Sdelphij	/* absolute timeout checks. Not 'time_t' by intention! */
610294904Sdelphij	uint32_t tobase;	/* base value for timeout */
611294904Sdelphij	uint32_t tospan;	/* timeout span (max delay) */
612294904Sdelphij	uint32_t todiff;	/* current delay */
61354359Sroberto
61454359Sroberto	/*
61554359Sroberto	 * This is pretty tricky.  We may get between 1 and many packets
61654359Sroberto	 * back in response to the request.  We peel the data out of
61754359Sroberto	 * each packet and collect it in one long block.  When the last
61854359Sroberto	 * packet in the sequence is received we'll know how many we
61954359Sroberto	 * should have had.  Note we use one long time out, should reconsider.
62054359Sroberto	 */
62154359Sroberto	*ritems = 0;
62254359Sroberto	*rsize = 0;
62354359Sroberto	firstpkt = 1;
62454359Sroberto	numrecv = 0;
62554359Sroberto	*rdata = datap = pktdata;
62654359Sroberto	lastseq = 999;	/* too big to be a sequence number */
627290000Sglebius	ZERO(haveseq);
62854359Sroberto	FD_ZERO(&fds);
629294904Sdelphij	tobase = (uint32_t)time(NULL);
63054359Sroberto
63154359Sroberto    again:
63254359Sroberto	if (firstpkt)
633290000Sglebius		tvo = tvout;
63454359Sroberto	else
635290000Sglebius		tvo = tvsout;
636294904Sdelphij	tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
63754359Sroberto
63854359Sroberto	FD_SET(sockfd, &fds);
639293894Sglebius	n = select(sockfd+1, &fds, NULL, NULL, &tvo);
64054359Sroberto	if (n == -1) {
641290000Sglebius		warning("select fails");
64254359Sroberto		return -1;
64354359Sroberto	}
644294904Sdelphij
645294904Sdelphij	/*
646294904Sdelphij	 * Check if this is already too late. Trash the data and fake a
647294904Sdelphij	 * timeout if this is so.
648294904Sdelphij	 */
649294904Sdelphij	todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
650294904Sdelphij	if ((n > 0) && (todiff > tospan)) {
651294904Sdelphij		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
652294904Sdelphij		n = 0; /* faked timeout return from 'select()'*/
653294904Sdelphij	}
654294904Sdelphij
65554359Sroberto	if (n == 0) {
65654359Sroberto		/*
65754359Sroberto		 * Timed out.  Return what we have
65854359Sroberto		 */
65954359Sroberto		if (firstpkt) {
66054359Sroberto			(void) fprintf(stderr,
661293894Sglebius				       "%s: timed out, nothing received\n",
662293894Sglebius				       currenthost);
66354359Sroberto			return ERR_TIMEOUT;
66454359Sroberto		} else {
66554359Sroberto			(void) fprintf(stderr,
66654359Sroberto				       "%s: timed out with incomplete data\n",
66754359Sroberto				       currenthost);
66854359Sroberto			if (debug) {
66954359Sroberto				printf("Received sequence numbers");
67054359Sroberto				for (n = 0; n <= MAXSEQ; n++)
67154359Sroberto				    if (haveseq[n])
672301301Sdelphij					printf(" %zd,", (size_t)n);
67354359Sroberto				if (lastseq != 999)
67454359Sroberto				    printf(" last frame received\n");
67554359Sroberto				else
67654359Sroberto				    printf(" last frame not received\n");
67754359Sroberto			}
67854359Sroberto			return ERR_INCOMPLETE;
67954359Sroberto		}
68054359Sroberto	}
68154359Sroberto
68254359Sroberto	n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
68354359Sroberto	if (n == -1) {
684290000Sglebius		warning("read");
68554359Sroberto		return -1;
68654359Sroberto	}
68754359Sroberto
68854359Sroberto
68954359Sroberto	/*
69054359Sroberto	 * Check for format errors.  Bug proofing.
69154359Sroberto	 */
692290000Sglebius	if (n < (ssize_t)RESP_HEADER_SIZE) {
69354359Sroberto		if (debug)
694301301Sdelphij			printf("Short (%zd byte) packet received\n", (size_t)n);
69554359Sroberto		goto again;
69654359Sroberto	}
69754359Sroberto	if (INFO_VERSION(rpkt.rm_vn_mode) > NTP_VERSION ||
69854359Sroberto	    INFO_VERSION(rpkt.rm_vn_mode) < NTP_OLDVERSION) {
69954359Sroberto		if (debug)
700290000Sglebius			printf("Packet received with version %d\n",
701290000Sglebius			       INFO_VERSION(rpkt.rm_vn_mode));
70254359Sroberto		goto again;
70354359Sroberto	}
70454359Sroberto	if (INFO_MODE(rpkt.rm_vn_mode) != MODE_PRIVATE) {
70554359Sroberto		if (debug)
706290000Sglebius			printf("Packet received with mode %d\n",
707290000Sglebius			       INFO_MODE(rpkt.rm_vn_mode));
70854359Sroberto		goto again;
70954359Sroberto	}
71054359Sroberto	if (INFO_IS_AUTH(rpkt.auth_seq)) {
71154359Sroberto		if (debug)
712290000Sglebius			printf("Encrypted packet received\n");
71354359Sroberto		goto again;
71454359Sroberto	}
71554359Sroberto	if (!ISRESPONSE(rpkt.rm_vn_mode)) {
71654359Sroberto		if (debug)
717290000Sglebius			printf("Received request packet, wanted response\n");
71854359Sroberto		goto again;
71954359Sroberto	}
72054359Sroberto	if (INFO_MBZ(rpkt.mbz_itemsize) != 0) {
72154359Sroberto		if (debug)
722290000Sglebius			printf("Received packet with nonzero MBZ field!\n");
72354359Sroberto		goto again;
72454359Sroberto	}
72554359Sroberto
72654359Sroberto	/*
72754359Sroberto	 * Check implementation/request.  Could be old data getting to us.
72854359Sroberto	 */
72954359Sroberto	if (rpkt.implementation != implcode || rpkt.request != reqcode) {
73054359Sroberto		if (debug)
731290000Sglebius			printf(
73254359Sroberto			    "Received implementation/request of %d/%d, wanted %d/%d",
73354359Sroberto			    rpkt.implementation, rpkt.request,
73454359Sroberto			    implcode, reqcode);
73554359Sroberto		goto again;
73654359Sroberto	}
73754359Sroberto
73854359Sroberto	/*
73954359Sroberto	 * Check the error code.  If non-zero, return it.
74054359Sroberto	 */
74154359Sroberto	if (INFO_ERR(rpkt.err_nitems) != INFO_OKAY) {
74254359Sroberto		if (debug && ISMORE(rpkt.rm_vn_mode)) {
74354359Sroberto			printf("Error code %d received on not-final packet\n",
74454359Sroberto			       INFO_ERR(rpkt.err_nitems));
74554359Sroberto		}
74654359Sroberto		return (int)INFO_ERR(rpkt.err_nitems);
74754359Sroberto	}
74854359Sroberto
74954359Sroberto	/*
75054359Sroberto	 * Collect items and size.  Make sure they make sense.
75154359Sroberto	 */
75254359Sroberto	items = INFO_NITEMS(rpkt.err_nitems);
75354359Sroberto	size = INFO_ITEMSIZE(rpkt.mbz_itemsize);
754132451Sroberto	if (esize > size)
755132451Sroberto		pad = esize - size;
756132451Sroberto	else
757132451Sroberto		pad = 0;
758290000Sglebius	datasize = items * size;
759290000Sglebius	if ((size_t)datasize > (n-RESP_HEADER_SIZE)) {
76054359Sroberto		if (debug)
76154359Sroberto		    printf(
762293894Sglebius			    "Received items %zu, size %zu (total %zu), data in packet is %zu\n",
76354359Sroberto			    items, size, datasize, n-RESP_HEADER_SIZE);
76454359Sroberto		goto again;
76554359Sroberto	}
76654359Sroberto
76754359Sroberto	/*
76854359Sroberto	 * If this isn't our first packet, make sure the size matches
76954359Sroberto	 * the other ones.
77054359Sroberto	 */
771290000Sglebius	if (!firstpkt && size != *rsize) {
77254359Sroberto		if (debug)
773293894Sglebius		    printf("Received itemsize %zu, previous %zu\n",
77454359Sroberto			   size, *rsize);
77554359Sroberto		goto again;
77654359Sroberto	}
77754359Sroberto	/*
778132451Sroberto	 * If we've received this before, +toss it
77954359Sroberto	 */
78054359Sroberto	seq = INFO_SEQ(rpkt.auth_seq);
78154359Sroberto	if (haveseq[seq]) {
78254359Sroberto		if (debug)
78354359Sroberto		    printf("Received duplicate sequence number %d\n", seq);
78454359Sroberto		goto again;
78554359Sroberto	}
78654359Sroberto	haveseq[seq] = 1;
78754359Sroberto
78854359Sroberto	/*
78954359Sroberto	 * If this is the last in the sequence, record that.
79054359Sroberto	 */
79154359Sroberto	if (!ISMORE(rpkt.rm_vn_mode)) {
79254359Sroberto		if (lastseq != 999) {
79354359Sroberto			printf("Received second end sequence packet\n");
79454359Sroberto			goto again;
79554359Sroberto		}
79654359Sroberto		lastseq = seq;
79754359Sroberto	}
79854359Sroberto
79954359Sroberto	/*
800294904Sdelphij	 * So far, so good.  Copy this data into the output array. Bump
801294904Sdelphij	 * the timeout base, in case we expect more data.
80254359Sroberto	 */
803294904Sdelphij	tobase = (uint32_t)time(NULL);
804132451Sroberto	if ((datap + datasize + (pad * items)) > (pktdata + pktdatasize)) {
805293894Sglebius		size_t offset = datap - pktdata;
80654359Sroberto		growpktdata();
807290000Sglebius		*rdata = pktdata; /* might have been realloced ! */
80854359Sroberto		datap = pktdata + offset;
80954359Sroberto	}
810132451Sroberto	/*
811132451Sroberto	 * We now move the pointer along according to size and number of
812132451Sroberto	 * items.  This is so we can play nice with older implementations
813132451Sroberto	 */
814132451Sroberto
815290000Sglebius	tmp_data = rpkt.u.data;
816290000Sglebius	for (i = 0; i < items; i++) {
817290000Sglebius		memcpy(datap, tmp_data, (unsigned)size);
818132451Sroberto		tmp_data += size;
819290000Sglebius		zero_mem(datap + size, pad);
820132451Sroberto		datap += size + pad;
821132451Sroberto	}
822132451Sroberto
82354359Sroberto	if (firstpkt) {
82454359Sroberto		firstpkt = 0;
825132451Sroberto		*rsize = size + pad;
82654359Sroberto	}
82754359Sroberto	*ritems += items;
82854359Sroberto
82954359Sroberto	/*
83054359Sroberto	 * Finally, check the count of received packets.  If we've got them
83154359Sroberto	 * all, return
83254359Sroberto	 */
83354359Sroberto	++numrecv;
83454359Sroberto	if (numrecv <= lastseq)
835290000Sglebius		goto again;
83654359Sroberto	return INFO_OKAY;
83754359Sroberto}
83854359Sroberto
83954359Sroberto
84054359Sroberto/*
84154359Sroberto * sendrequest - format and send a request packet
842290000Sglebius *
843290000Sglebius * Historically, ntpdc has used a fixed-size request packet regardless
844290000Sglebius * of the actual payload size.  When authenticating, the timestamp, key
845290000Sglebius * ID, and digest have been placed just before the end of the packet.
846290000Sglebius * With the introduction in late 2009 of support for authenticated
847290000Sglebius * ntpdc requests using larger 20-octet digests (vs. 16 for MD5), we
848290000Sglebius * come up four bytes short.
849290000Sglebius *
850290000Sglebius * To maintain interop while allowing for larger digests, the behavior
851290000Sglebius * is unchanged when using 16-octet digests.  For larger digests, the
852290000Sglebius * timestamp, key ID, and digest are placed immediately following the
853290000Sglebius * request payload, with the overall packet size variable.  ntpd can
854290000Sglebius * distinguish 16-octet digests by the overall request size being
855290000Sglebius * REQ_LEN_NOMAC + 4 + 16 with the auth bit enabled.  When using a
856290000Sglebius * longer digest, that request size should be avoided.
857290000Sglebius *
858290000Sglebius * With the form used with 20-octet and larger digests, the timestamp,
859290000Sglebius * key ID, and digest are located by ntpd relative to the start of the
860290000Sglebius * packet, and the size of the digest is then implied by the packet
861290000Sglebius * size.
86254359Sroberto */
86354359Srobertostatic int
86454359Srobertosendrequest(
86554359Sroberto	int implcode,
86654359Sroberto	int reqcode,
86754359Sroberto	int auth,
868293894Sglebius	size_t qitems,
869290000Sglebius	size_t qsize,
870293894Sglebius	const char *qdata
87154359Sroberto	)
87254359Sroberto{
87354359Sroberto	struct req_pkt qpkt;
874290000Sglebius	size_t	datasize;
875290000Sglebius	size_t	reqsize;
876290000Sglebius	u_long	key_id;
877290000Sglebius	l_fp	ts;
878290000Sglebius	l_fp *	ptstamp;
879293894Sglebius	size_t	maclen;
880290000Sglebius	char *	pass;
88154359Sroberto
882290000Sglebius	ZERO(qpkt);
88354359Sroberto	qpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
88454359Sroberto	qpkt.implementation = (u_char)implcode;
88554359Sroberto	qpkt.request = (u_char)reqcode;
88654359Sroberto
88754359Sroberto	datasize = qitems * qsize;
888290000Sglebius	if (datasize && qdata != NULL) {
889290000Sglebius		memcpy(qpkt.u.data, qdata, datasize);
89054359Sroberto		qpkt.err_nitems = ERR_NITEMS(0, qitems);
89154359Sroberto		qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);
89254359Sroberto	} else {
89354359Sroberto		qpkt.err_nitems = ERR_NITEMS(0, 0);
894182007Sroberto		qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);  /* allow for optional first item */
89554359Sroberto	}
89654359Sroberto
897182007Sroberto	if (!auth || (keyid_entered && info_auth_keyid == 0)) {
89854359Sroberto		qpkt.auth_seq = AUTH_SEQ(0, 0);
899290000Sglebius		return sendpkt(&qpkt, req_pkt_size);
900290000Sglebius	}
90154359Sroberto
902290000Sglebius	if (info_auth_keyid == 0) {
903290000Sglebius		key_id = getkeyid("Keyid: ");
904290000Sglebius		if (!key_id) {
905290000Sglebius			fprintf(stderr, "Invalid key identifier\n");
906290000Sglebius			return 1;
90754359Sroberto		}
908290000Sglebius		info_auth_keyid = key_id;
909290000Sglebius	}
910290000Sglebius	if (!authistrusted(info_auth_keyid)) {
911290000Sglebius		pass = getpass_keytype(info_auth_keytype);
912290000Sglebius		if ('\0' == pass[0]) {
913290000Sglebius			fprintf(stderr, "Invalid password\n");
914290000Sglebius			return 1;
91554359Sroberto		}
916290000Sglebius		authusekey(info_auth_keyid, info_auth_keytype,
917290000Sglebius			   (u_char *)pass);
91854359Sroberto		authtrust(info_auth_keyid, 1);
91954359Sroberto	}
920290000Sglebius	qpkt.auth_seq = AUTH_SEQ(1, 0);
921290000Sglebius	if (info_auth_hashlen > 16) {
922290000Sglebius		/*
923290000Sglebius		 * Only ntpd which expects REQ_LEN_NOMAC plus maclen
924290000Sglebius		 * octets in an authenticated request using a 16 octet
925290000Sglebius		 * digest (that is, a newer ntpd) will handle digests
926290000Sglebius		 * larger than 16 octets, so for longer digests, do
927290000Sglebius		 * not attempt to shorten the requests for downlevel
928290000Sglebius		 * ntpd compatibility.
929290000Sglebius		 */
930290000Sglebius		if (REQ_LEN_NOMAC != req_pkt_size)
931290000Sglebius			return 1;
932290000Sglebius		reqsize = REQ_LEN_HDR + datasize + sizeof(*ptstamp);
933290000Sglebius		/* align to 32 bits */
934290000Sglebius		reqsize = (reqsize + 3) & ~3;
935290000Sglebius	} else
936290000Sglebius		reqsize = req_pkt_size;
937290000Sglebius	ptstamp = (void *)((char *)&qpkt + reqsize);
938290000Sglebius	ptstamp--;
939290000Sglebius	get_systime(&ts);
940290000Sglebius	L_ADD(&ts, &delay_time);
941290000Sglebius	HTONL_FP(&ts, ptstamp);
942293894Sglebius	maclen = authencrypt(
943293894Sglebius		info_auth_keyid, (void *)&qpkt, size2int_chk(reqsize));
944290000Sglebius	if (!maclen) {
945290000Sglebius		fprintf(stderr, "Key not found\n");
946290000Sglebius		return 1;
947290000Sglebius	} else if (maclen != (int)(info_auth_hashlen + sizeof(keyid_t))) {
948290000Sglebius		fprintf(stderr,
949293894Sglebius			"%zu octet MAC, %zu expected with %zu octet digest\n",
950290000Sglebius			maclen, (info_auth_hashlen + sizeof(keyid_t)),
951290000Sglebius			info_auth_hashlen);
952290000Sglebius		return 1;
953290000Sglebius	}
954290000Sglebius	return sendpkt(&qpkt, reqsize + maclen);
95554359Sroberto}
95654359Sroberto
95754359Sroberto
95854359Sroberto/*
95954359Sroberto * doquery - send a request and process the response
96054359Sroberto */
96154359Srobertoint
96254359Srobertodoquery(
96354359Sroberto	int implcode,
96454359Sroberto	int reqcode,
96554359Sroberto	int auth,
966293894Sglebius	size_t qitems,
967293894Sglebius	size_t qsize,
968293894Sglebius	const char *qdata,
969293894Sglebius	size_t *ritems,
970293894Sglebius	size_t *rsize,
971293894Sglebius	const char **rdata,
972132451Sroberto 	int quiet_mask,
973132451Sroberto	int esize
97454359Sroberto	)
97554359Sroberto{
97654359Sroberto	int res;
97754359Sroberto	char junk[512];
97854359Sroberto	fd_set fds;
979290000Sglebius	struct sock_timeval tvzero;
98054359Sroberto
98154359Sroberto	/*
98254359Sroberto	 * Check to make sure host is open
98354359Sroberto	 */
98454359Sroberto	if (!havehost) {
98554359Sroberto		(void) fprintf(stderr, "***No host open, use `host' command\n");
98654359Sroberto		return -1;
98754359Sroberto	}
98854359Sroberto
98954359Sroberto	/*
99054359Sroberto	 * Poll the socket and clear out any pending data
99154359Sroberto	 */
992106163Srobertoagain:
99354359Sroberto	do {
99454359Sroberto		tvzero.tv_sec = tvzero.tv_usec = 0;
99554359Sroberto		FD_ZERO(&fds);
99654359Sroberto		FD_SET(sockfd, &fds);
997293894Sglebius		res = select(sockfd+1, &fds, NULL, NULL, &tvzero);
99854359Sroberto		if (res == -1) {
999290000Sglebius			warning("polling select");
100054359Sroberto			return -1;
100154359Sroberto		} else if (res > 0)
100254359Sroberto
100354359Sroberto		    (void) recv(sockfd, junk, sizeof junk, 0);
100454359Sroberto	} while (res > 0);
100554359Sroberto
100654359Sroberto
100754359Sroberto	/*
100854359Sroberto	 * send a request
100954359Sroberto	 */
101054359Sroberto	res = sendrequest(implcode, reqcode, auth, qitems, qsize, qdata);
101154359Sroberto	if (res != 0)
1012290000Sglebius		return res;
101354359Sroberto
101454359Sroberto	/*
101554359Sroberto	 * Get the response.  If we got a standard error, print a message
101654359Sroberto	 */
1017132451Sroberto	res = getresponse(implcode, reqcode, ritems, rsize, rdata, esize);
101854359Sroberto
1019106163Sroberto	/*
1020106163Sroberto	 * Try to be compatible with older implementations of ntpd.
1021106163Sroberto	 */
1022106163Sroberto	if (res == INFO_ERR_FMT && req_pkt_size != 48) {
1023106163Sroberto		int oldsize;
1024106163Sroberto
1025106163Sroberto		oldsize = req_pkt_size;
1026106163Sroberto
1027106163Sroberto		switch(req_pkt_size) {
1028106163Sroberto		case REQ_LEN_NOMAC:
1029132451Sroberto			req_pkt_size = 160;
1030132451Sroberto			break;
1031132451Sroberto		case 160:
1032106163Sroberto			req_pkt_size = 48;
1033106163Sroberto			break;
1034106163Sroberto		}
1035132451Sroberto		if (impl_ver == IMPL_XNTPD) {
1036132451Sroberto			fprintf(stderr,
1037132451Sroberto			    "***Warning changing to older implementation\n");
1038132451Sroberto			return INFO_ERR_IMPL;
1039132451Sroberto		}
1040106163Sroberto
1041106163Sroberto		fprintf(stderr,
1042106163Sroberto		    "***Warning changing the request packet size from %d to %d\n",
1043106163Sroberto		    oldsize, req_pkt_size);
1044106163Sroberto		goto again;
1045106163Sroberto	}
1046106163Sroberto
104754359Sroberto 	/* log error message if not told to be quiet */
104854359Sroberto 	if ((res > 0) && (((1 << res) & quiet_mask) == 0)) {
104954359Sroberto		switch(res) {
1050290000Sglebius		case INFO_ERR_IMPL:
1051132451Sroberto			/* Give us a chance to try the older implementation. */
1052132451Sroberto			if (implcode == IMPL_XNTPD)
1053132451Sroberto				break;
105454359Sroberto			(void) fprintf(stderr,
1055290000Sglebius				       "***Server implementation incompatible with our own\n");
105654359Sroberto			break;
1057290000Sglebius		case INFO_ERR_REQ:
105854359Sroberto			(void) fprintf(stderr,
105954359Sroberto				       "***Server doesn't implement this request\n");
106054359Sroberto			break;
1061290000Sglebius		case INFO_ERR_FMT:
106254359Sroberto			(void) fprintf(stderr,
106354359Sroberto				       "***Server reports a format error in the received packet (shouldn't happen)\n");
106454359Sroberto			break;
1065290000Sglebius		case INFO_ERR_NODATA:
106654359Sroberto			(void) fprintf(stderr,
106754359Sroberto				       "***Server reports data not found\n");
106854359Sroberto			break;
1069290000Sglebius		case INFO_ERR_AUTH:
107054359Sroberto			(void) fprintf(stderr, "***Permission denied\n");
107154359Sroberto			break;
1072290000Sglebius		case ERR_TIMEOUT:
107354359Sroberto			(void) fprintf(stderr, "***Request timed out\n");
107454359Sroberto			break;
1075290000Sglebius		case ERR_INCOMPLETE:
107654359Sroberto			(void) fprintf(stderr,
107754359Sroberto				       "***Response from server was incomplete\n");
107854359Sroberto			break;
1079290000Sglebius		default:
108054359Sroberto			(void) fprintf(stderr,
108154359Sroberto				       "***Server returns unknown error code %d\n", res);
108254359Sroberto			break;
108354359Sroberto		}
108454359Sroberto	}
108554359Sroberto	return res;
108654359Sroberto}
108754359Sroberto
108854359Sroberto
108954359Sroberto/*
109054359Sroberto * getcmds - read commands from the standard input and execute them
109154359Sroberto */
109254359Srobertostatic void
109354359Srobertogetcmds(void)
109454359Sroberto{
1095290000Sglebius	char *	line;
1096290000Sglebius	int	count;
109782498Sroberto
1098290000Sglebius	ntp_readline_init(interactive ? prompt : NULL);
1099290000Sglebius
110082498Sroberto	for (;;) {
1101290000Sglebius		line = ntp_readline(&count);
1102290000Sglebius		if (NULL == line)
1103290000Sglebius			break;
110482498Sroberto		docmd(line);
110582498Sroberto		free(line);
110682498Sroberto	}
110754359Sroberto
1108290000Sglebius	ntp_readline_uninit();
110954359Sroberto}
111054359Sroberto
111154359Sroberto
1112132451Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
111354359Sroberto/*
111454359Sroberto * abortcmd - catch interrupts and abort the current command
111554359Sroberto */
111654359Srobertostatic RETSIGTYPE
111754359Srobertoabortcmd(
111854359Sroberto	int sig
111954359Sroberto	)
112054359Sroberto{
112154359Sroberto
112254359Sroberto	if (current_output == stdout)
112354359Sroberto	    (void) fflush(stdout);
112454359Sroberto	putc('\n', stderr);
112554359Sroberto	(void) fflush(stderr);
112654359Sroberto	if (jump) longjmp(interrupt_buf, 1);
112754359Sroberto}
1128132451Sroberto#endif /* SYS_WINNT */
112954359Sroberto
113054359Sroberto/*
113154359Sroberto * docmd - decode the command line and execute a command
113254359Sroberto */
113354359Srobertostatic void
113454359Srobertodocmd(
113554359Sroberto	const char *cmdline
113654359Sroberto	)
113754359Sroberto{
1138182007Sroberto	char *tokens[1+MAXARGS+MOREARGS+2];
113954359Sroberto	struct parse pcmd;
114054359Sroberto	int ntok;
1141132451Sroberto	int i, ti;
1142132451Sroberto	int rval;
114354359Sroberto	struct xcmd *xcmd;
114454359Sroberto
1145132451Sroberto	ai_fam_templ = ai_fam_default;
114654359Sroberto	/*
114754359Sroberto	 * Tokenize the command line.  If nothing on it, return.
114854359Sroberto	 */
1149290000Sglebius	if (strlen(cmdline) >= MAXLINE) {
1150290000Sglebius		fprintf(stderr, "***Command ignored, more than %d characters:\n%s\n",
1151290000Sglebius			MAXLINE - 1, cmdline);
1152290000Sglebius		return;
1153290000Sglebius	}
115454359Sroberto	tokenize(cmdline, tokens, &ntok);
115554359Sroberto	if (ntok == 0)
115654359Sroberto	    return;
115754359Sroberto
115854359Sroberto	/*
115954359Sroberto	 * Find the appropriate command description.
116054359Sroberto	 */
116154359Sroberto	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
116254359Sroberto	if (i == 0) {
116354359Sroberto		(void) fprintf(stderr, "***Command `%s' unknown\n",
116454359Sroberto			       tokens[0]);
116554359Sroberto		return;
116654359Sroberto	} else if (i >= 2) {
116754359Sroberto		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
116854359Sroberto			       tokens[0]);
116954359Sroberto		return;
117054359Sroberto	}
117154359Sroberto
117254359Sroberto	/*
117354359Sroberto	 * Save the keyword, then walk through the arguments, interpreting
117454359Sroberto	 * as we go.
117554359Sroberto	 */
117654359Sroberto	pcmd.keyword = tokens[0];
117754359Sroberto	pcmd.nargs = 0;
1178132451Sroberto	ti = 1;
1179132451Sroberto	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO;) {
1180132451Sroberto		if ((i+ti) >= ntok) {
118154359Sroberto			if (!(xcmd->arg[i] & OPT)) {
118254359Sroberto				printusage(xcmd, stderr);
118354359Sroberto				return;
118454359Sroberto			}
118554359Sroberto			break;
118654359Sroberto		}
1187132451Sroberto		if ((xcmd->arg[i] & OPT) && (*tokens[i+ti] == '>'))
1188132451Sroberto			break;
1189132451Sroberto		rval = getarg(tokens[i+ti], (int)xcmd->arg[i], &pcmd.argval[i]);
1190132451Sroberto		if (rval == -1) {
1191132451Sroberto			ti++;
1192132451Sroberto			continue;
1193132451Sroberto		}
1194132451Sroberto		if (rval == 0)
1195132451Sroberto			return;
119654359Sroberto		pcmd.nargs++;
1197132451Sroberto		i++;
119854359Sroberto	}
119954359Sroberto
1200182007Sroberto	/* Any extra args are assumed to be "OPT|NTP_STR". */
1201182007Sroberto	for ( ; i < MAXARGS + MOREARGS;) {
1202182007Sroberto	     if ((i+ti) >= ntok)
1203182007Sroberto		  break;
1204182007Sroberto		rval = getarg(tokens[i+ti], (int)(OPT|NTP_STR), &pcmd.argval[i]);
1205182007Sroberto		if (rval == -1) {
1206182007Sroberto			ti++;
1207182007Sroberto			continue;
1208182007Sroberto		}
1209182007Sroberto		if (rval == 0)
1210182007Sroberto			return;
1211182007Sroberto		pcmd.nargs++;
1212182007Sroberto		i++;
1213182007Sroberto	}
1214182007Sroberto
1215132451Sroberto	i += ti;
121654359Sroberto	if (i < ntok && *tokens[i] == '>') {
121754359Sroberto		char *fname;
121854359Sroberto
121954359Sroberto		if (*(tokens[i]+1) != '\0')
122054359Sroberto		    fname = tokens[i]+1;
122154359Sroberto		else if ((i+1) < ntok)
122254359Sroberto		    fname = tokens[i+1];
122354359Sroberto		else {
122454359Sroberto			(void) fprintf(stderr, "***No file for redirect\n");
122554359Sroberto			return;
122654359Sroberto		}
122754359Sroberto
122854359Sroberto		current_output = fopen(fname, "w");
122954359Sroberto		if (current_output == NULL) {
123054359Sroberto			(void) fprintf(stderr, "***Error opening %s: ", fname);
123154359Sroberto			perror("");
123254359Sroberto			return;
123354359Sroberto		}
123454359Sroberto	} else {
123554359Sroberto		current_output = stdout;
123654359Sroberto	}
123754359Sroberto
123854359Sroberto	if (interactive && setjmp(interrupt_buf)) {
123954359Sroberto		return;
124054359Sroberto	} else {
124154359Sroberto		jump = 1;
124254359Sroberto		(xcmd->handler)(&pcmd, current_output);
124354359Sroberto		jump = 0;
1244132451Sroberto		if (current_output != stdout)
1245132451Sroberto			(void) fclose(current_output);
1246132451Sroberto		current_output = NULL;
124754359Sroberto	}
124854359Sroberto}
124954359Sroberto
125054359Sroberto
125154359Sroberto/*
125254359Sroberto * tokenize - turn a command line into tokens
125354359Sroberto */
125454359Srobertostatic void
125554359Srobertotokenize(
125654359Sroberto	const char *line,
125754359Sroberto	char **tokens,
125854359Sroberto	int *ntok
125954359Sroberto	)
126054359Sroberto{
126154359Sroberto	register const char *cp;
126254359Sroberto	register char *sp;
126354359Sroberto	static char tspace[MAXLINE];
126454359Sroberto
126554359Sroberto	sp = tspace;
126654359Sroberto	cp = line;
126754359Sroberto	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
126854359Sroberto		tokens[*ntok] = sp;
126954359Sroberto		while (ISSPACE(*cp))
127054359Sroberto		    cp++;
127154359Sroberto		if (ISEOL(*cp))
127254359Sroberto		    break;
127354359Sroberto		do {
127454359Sroberto			*sp++ = *cp++;
127554359Sroberto		} while (!ISSPACE(*cp) && !ISEOL(*cp));
127654359Sroberto
127754359Sroberto		*sp++ = '\0';
127854359Sroberto	}
127954359Sroberto}
128054359Sroberto
128154359Sroberto
128254359Sroberto
128354359Sroberto/*
128454359Sroberto * findcmd - find a command in a command description table
128554359Sroberto */
128654359Srobertostatic int
128754359Srobertofindcmd(
128854359Sroberto	register char *str,
128954359Sroberto	struct xcmd *clist1,
129054359Sroberto	struct xcmd *clist2,
129154359Sroberto	struct xcmd **cmd
129254359Sroberto	)
129354359Sroberto{
129454359Sroberto	register struct xcmd *cl;
1295293894Sglebius	size_t clen;
129654359Sroberto	int nmatch;
129754359Sroberto	struct xcmd *nearmatch = NULL;
129854359Sroberto	struct xcmd *clist;
129954359Sroberto
130054359Sroberto	clen = strlen(str);
130154359Sroberto	nmatch = 0;
130254359Sroberto	if (clist1 != 0)
130354359Sroberto	    clist = clist1;
130454359Sroberto	else if (clist2 != 0)
130554359Sroberto	    clist = clist2;
130654359Sroberto	else
130754359Sroberto	    return 0;
130854359Sroberto
130954359Sroberto    again:
131054359Sroberto	for (cl = clist; cl->keyword != 0; cl++) {
131154359Sroberto		/* do a first character check, for efficiency */
131254359Sroberto		if (*str != *(cl->keyword))
131354359Sroberto		    continue;
131454359Sroberto		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
131554359Sroberto			/*
131654359Sroberto			 * Could be extact match, could be approximate.
131754359Sroberto			 * Is exact if the length of the keyword is the
131854359Sroberto			 * same as the str.
131954359Sroberto			 */
132054359Sroberto			if (*((cl->keyword) + clen) == '\0') {
132154359Sroberto				*cmd = cl;
132254359Sroberto				return 1;
132354359Sroberto			}
132454359Sroberto			nmatch++;
132554359Sroberto			nearmatch = cl;
132654359Sroberto		}
132754359Sroberto	}
132854359Sroberto
132954359Sroberto				/*
133054359Sroberto				 * See if there is more to do.  If so, go again.  Sorry about the
133154359Sroberto				 * goto, too much looking at BSD sources...
133254359Sroberto				 */
133354359Sroberto	if (clist == clist1 && clist2 != 0) {
133454359Sroberto		clist = clist2;
133554359Sroberto		goto again;
133654359Sroberto	}
133754359Sroberto
133854359Sroberto				/*
133954359Sroberto				 * If we got extactly 1 near match, use it, else return number
134054359Sroberto				 * of matches.
134154359Sroberto				 */
134254359Sroberto	if (nmatch == 1) {
134354359Sroberto		*cmd = nearmatch;
134454359Sroberto		return 1;
134554359Sroberto	}
134654359Sroberto	return nmatch;
134754359Sroberto}
134854359Sroberto
134954359Sroberto
1350132451Sroberto/*
135154359Sroberto * getarg - interpret an argument token
1352132451Sroberto *
1353182007Sroberto * string is always set.
1354182007Sroberto * type is set to the decoded type.
1355182007Sroberto *
1356132451Sroberto * return:	 0 - failure
1357132451Sroberto *		 1 - success
1358132451Sroberto *		-1 - skip to next token
135954359Sroberto */
136054359Srobertostatic int
136154359Srobertogetarg(
136254359Sroberto	char *str,
136354359Sroberto	int code,
136454359Sroberto	arg_v *argp
136554359Sroberto	)
136654359Sroberto{
136754359Sroberto	int isneg;
136854359Sroberto	char *cp, *np;
136954359Sroberto	static const char *digits = "0123456789";
137054359Sroberto
1371290000Sglebius	ZERO(*argp);
1372182007Sroberto	argp->string = str;
1373182007Sroberto	argp->type   = code & ~OPT;
1374182007Sroberto
1375182007Sroberto	switch (argp->type) {
137654359Sroberto	    case NTP_STR:
137754359Sroberto		break;
1378182007Sroberto	    case NTP_ADD:
1379132451Sroberto		if (!strcmp("-6", str)) {
1380132451Sroberto			ai_fam_templ = AF_INET6;
1381132451Sroberto			return -1;
1382132451Sroberto		} else if (!strcmp("-4", str)) {
1383132451Sroberto			ai_fam_templ = AF_INET;
1384132451Sroberto			return -1;
1385132451Sroberto		}
1386132451Sroberto		if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) {
138754359Sroberto			return 0;
138854359Sroberto		}
138954359Sroberto		break;
1390182007Sroberto	    case NTP_INT:
1391182007Sroberto	    case NTP_UINT:
139254359Sroberto		isneg = 0;
139354359Sroberto		np = str;
139454359Sroberto		if (*np == '-') {
139554359Sroberto			np++;
139654359Sroberto			isneg = 1;
139754359Sroberto		}
139854359Sroberto
139954359Sroberto		argp->uval = 0;
140054359Sroberto		do {
140154359Sroberto			cp = strchr(digits, *np);
140254359Sroberto			if (cp == NULL) {
140354359Sroberto				(void) fprintf(stderr,
140454359Sroberto					       "***Illegal integer value %s\n", str);
140554359Sroberto				return 0;
140654359Sroberto			}
140754359Sroberto			argp->uval *= 10;
1408293894Sglebius			argp->uval += (u_long)(cp - digits);
140954359Sroberto		} while (*(++np) != '\0');
141054359Sroberto
141154359Sroberto		if (isneg) {
1412182007Sroberto			if ((code & ~OPT) == NTP_UINT) {
141354359Sroberto				(void) fprintf(stderr,
141454359Sroberto					       "***Value %s should be unsigned\n", str);
141554359Sroberto				return 0;
141654359Sroberto			}
141754359Sroberto			argp->ival = -argp->ival;
141854359Sroberto		}
141954359Sroberto		break;
1420132451Sroberto	    case IP_VERSION:
1421132451Sroberto		if (!strcmp("-6", str))
1422132451Sroberto			argp->ival = 6 ;
1423132451Sroberto		else if (!strcmp("-4", str))
1424132451Sroberto			argp->ival = 4 ;
1425132451Sroberto		else {
1426132451Sroberto			(void) fprintf(stderr,
1427132451Sroberto			    "***Version must be either 4 or 6\n");
1428132451Sroberto			return 0;
1429132451Sroberto		}
1430132451Sroberto		break;
143154359Sroberto	}
143254359Sroberto
143354359Sroberto	return 1;
143454359Sroberto}
143554359Sroberto
143654359Sroberto
143754359Sroberto/*
143854359Sroberto * getnetnum - given a host name, return its net number
143954359Sroberto *	       and (optional) full name
144054359Sroberto */
144154359Srobertostatic int
144254359Srobertogetnetnum(
144354359Sroberto	const char *hname,
1444290000Sglebius	sockaddr_u *num,
1445132451Sroberto	char *fullhost,
1446132451Sroberto	int af
144754359Sroberto	)
144854359Sroberto{
1449132451Sroberto	struct addrinfo hints, *ai = NULL;
145054359Sroberto
1451290000Sglebius	ZERO(hints);
1452132451Sroberto	hints.ai_flags = AI_CANONNAME;
1453132451Sroberto#ifdef AI_ADDRCONFIG
1454132451Sroberto	hints.ai_flags |= AI_ADDRCONFIG;
1455132451Sroberto#endif
1456132451Sroberto
1457290000Sglebius	/*
1458290000Sglebius	 * decodenetnum only works with addresses, but handles syntax
1459290000Sglebius	 * that getaddrinfo doesn't:  [2001::1]:1234
1460290000Sglebius	 */
146154359Sroberto	if (decodenetnum(hname, num)) {
1462290000Sglebius		if (fullhost != NULL)
1463290000Sglebius			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1464290000Sglebius				    LENHOSTNAME, NULL, 0, 0);
146554359Sroberto		return 1;
1466182007Sroberto	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1467290000Sglebius		INSIST(sizeof(*num) >= ai->ai_addrlen);
1468290000Sglebius		memcpy(num, ai->ai_addr, ai->ai_addrlen);
1469290000Sglebius		if (fullhost != NULL) {
1470290000Sglebius			if (ai->ai_canonname != NULL)
1471290000Sglebius				strlcpy(fullhost, ai->ai_canonname,
1472290000Sglebius					LENHOSTNAME);
1473290000Sglebius			else
1474290000Sglebius				getnameinfo(&num->sa, SOCKLEN(num),
1475290000Sglebius					    fullhost, LENHOSTNAME, NULL,
1476290000Sglebius					    0, 0);
1477290000Sglebius		}
147854359Sroberto		return 1;
147954359Sroberto	}
1480290000Sglebius	fprintf(stderr, "***Can't find host %s\n", hname);
1481290000Sglebius
1482290000Sglebius	return 0;
148354359Sroberto}
148454359Sroberto
1485290000Sglebius
148654359Sroberto/*
148754359Sroberto * nntohost - convert network number to host name.  This routine enforces
148854359Sroberto *	       the showhostnames setting.
148954359Sroberto */
1490290000Sglebiusconst char *
149154359Srobertonntohost(
1492290000Sglebius	sockaddr_u *netnum
149354359Sroberto	)
149454359Sroberto{
1495290000Sglebius	if (!showhostnames || SOCK_UNSPEC(netnum))
1496290000Sglebius		return stoa(netnum);
1497290000Sglebius	else if (ISREFCLOCKADR(netnum))
1498132451Sroberto		return refnumtoa(netnum);
1499290000Sglebius	else
1500290000Sglebius		return socktohost(netnum);
150154359Sroberto}
150254359Sroberto
150354359Sroberto
150454359Sroberto/*
150554359Sroberto * Finally, the built in command handlers
150654359Sroberto */
150754359Sroberto
150854359Sroberto/*
150954359Sroberto * help - tell about commands, or details of a particular command
151054359Sroberto */
151154359Srobertostatic void
151254359Srobertohelp(
151354359Sroberto	struct parse *pcmd,
151454359Sroberto	FILE *fp
151554359Sroberto	)
151654359Sroberto{
151754359Sroberto	struct xcmd *xcp;
151854359Sroberto	char *cmd;
1519182007Sroberto	const char *list[100];
1520290000Sglebius	size_t word, words;
1521290000Sglebius	size_t row, rows;
1522290000Sglebius	size_t col, cols;
1523290000Sglebius	size_t length;
152454359Sroberto
152554359Sroberto	if (pcmd->nargs == 0) {
1526182007Sroberto		words = 0;
152754359Sroberto		for (xcp = builtins; xcp->keyword != 0; xcp++) {
152854359Sroberto			if (*(xcp->keyword) != '?')
1529290000Sglebius				list[words++] = xcp->keyword;
153054359Sroberto		}
1531290000Sglebius		for (xcp = opcmds; xcp->keyword != 0; xcp++)
1532290000Sglebius			list[words++] = xcp->keyword;
153354359Sroberto
1534290000Sglebius		qsort((void *)list, words, sizeof(list[0]), helpsort);
1535182007Sroberto		col = 0;
1536182007Sroberto		for (word = 0; word < words; word++) {
1537290000Sglebius			length = strlen(list[word]);
1538290000Sglebius			col = max(col, length);
153954359Sroberto		}
154054359Sroberto
1541182007Sroberto		cols = SCREENWIDTH / ++col;
1542290000Sglebius		rows = (words + cols - 1) / cols;
1543182007Sroberto
1544290000Sglebius		fprintf(fp, "ntpdc commands:\n");
1545182007Sroberto
1546182007Sroberto		for (row = 0; row < rows; row++) {
1547290000Sglebius			for (word = row; word < words; word += rows)
1548290000Sglebius				fprintf(fp, "%-*.*s", (int)col,
1549290000Sglebius					(int)col - 1, list[word]);
1550290000Sglebius			fprintf(fp, "\n");
155154359Sroberto		}
155254359Sroberto	} else {
155354359Sroberto		cmd = pcmd->argval[0].string;
1554182007Sroberto		words = findcmd(cmd, builtins, opcmds, &xcp);
1555182007Sroberto		if (words == 0) {
1556290000Sglebius			fprintf(stderr,
1557290000Sglebius				"Command `%s' is unknown\n", cmd);
155854359Sroberto			return;
1559182007Sroberto		} else if (words >= 2) {
1560290000Sglebius			fprintf(stderr,
1561290000Sglebius				"Command `%s' is ambiguous\n", cmd);
156254359Sroberto			return;
156354359Sroberto		}
1564290000Sglebius		fprintf(fp, "function: %s\n", xcp->comment);
156554359Sroberto		printusage(xcp, fp);
156654359Sroberto	}
156754359Sroberto}
156854359Sroberto
156954359Sroberto
157054359Sroberto/*
157154359Sroberto * helpsort - do hostname qsort comparisons
157254359Sroberto */
157354359Srobertostatic int
157454359Srobertohelpsort(
157554359Sroberto	const void *t1,
157654359Sroberto	const void *t2
157754359Sroberto	)
157854359Sroberto{
1579290000Sglebius	const char * const *	name1 = t1;
1580290000Sglebius	const char * const *	name2 = t2;
158154359Sroberto
158254359Sroberto	return strcmp(*name1, *name2);
158354359Sroberto}
158454359Sroberto
158554359Sroberto
158654359Sroberto/*
158754359Sroberto * printusage - print usage information for a command
158854359Sroberto */
158954359Srobertostatic void
159054359Srobertoprintusage(
159154359Sroberto	struct xcmd *xcp,
159254359Sroberto	FILE *fp
159354359Sroberto	)
159454359Sroberto{
1595132451Sroberto	int i, opt46;
159654359Sroberto
1597132451Sroberto	opt46 = 0;
159854359Sroberto	(void) fprintf(fp, "usage: %s", xcp->keyword);
159954359Sroberto	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
1600182007Sroberto		if (opt46 == 0 && (xcp->arg[i] & ~OPT) == NTP_ADD) {
1601132451Sroberto			(void) fprintf(fp, " [ -4|-6 ]");
1602132451Sroberto			opt46 = 1;
1603132451Sroberto		}
160454359Sroberto		if (xcp->arg[i] & OPT)
160554359Sroberto		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
160654359Sroberto		else
160754359Sroberto		    (void) fprintf(fp, " %s", xcp->desc[i]);
160854359Sroberto	}
160954359Sroberto	(void) fprintf(fp, "\n");
161054359Sroberto}
161154359Sroberto
161254359Sroberto
161354359Sroberto/*
161454359Sroberto * timeout - set time out time
161554359Sroberto */
161654359Srobertostatic void
161754359Srobertotimeout(
161854359Sroberto	struct parse *pcmd,
161954359Sroberto	FILE *fp
162054359Sroberto	)
162154359Sroberto{
162254359Sroberto	int val;
162354359Sroberto
162454359Sroberto	if (pcmd->nargs == 0) {
162554359Sroberto		val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
162654359Sroberto		(void) fprintf(fp, "primary timeout %d ms\n", val);
162754359Sroberto	} else {
162854359Sroberto		tvout.tv_sec = pcmd->argval[0].uval / 1000;
162954359Sroberto		tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
163054359Sroberto			* 1000;
163154359Sroberto	}
163254359Sroberto}
163354359Sroberto
163454359Sroberto
163554359Sroberto/*
163654359Sroberto * my_delay - set delay for auth requests
163754359Sroberto */
163854359Srobertostatic void
163954359Srobertomy_delay(
164054359Sroberto	struct parse *pcmd,
164154359Sroberto	FILE *fp
164254359Sroberto	)
164354359Sroberto{
164454359Sroberto	int isneg;
164554359Sroberto	u_long val;
164654359Sroberto
164754359Sroberto	if (pcmd->nargs == 0) {
164854359Sroberto		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
164954359Sroberto		(void) fprintf(fp, "delay %lu ms\n", val);
165054359Sroberto	} else {
165154359Sroberto		if (pcmd->argval[0].ival < 0) {
165254359Sroberto			isneg = 1;
165354359Sroberto			val = (u_long)(-pcmd->argval[0].ival);
165454359Sroberto		} else {
165554359Sroberto			isneg = 0;
165654359Sroberto			val = (u_long)pcmd->argval[0].ival;
165754359Sroberto		}
165854359Sroberto
165954359Sroberto		delay_time.l_ui = val / 1000;
166054359Sroberto		val %= 1000;
166154359Sroberto		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
166254359Sroberto
166354359Sroberto		if (isneg)
166454359Sroberto		    L_NEG(&delay_time);
166554359Sroberto	}
166654359Sroberto}
166754359Sroberto
166854359Sroberto
166954359Sroberto/*
167054359Sroberto * host - set the host we are dealing with.
167154359Sroberto */
167254359Srobertostatic void
167354359Srobertohost(
167454359Sroberto	struct parse *pcmd,
167554359Sroberto	FILE *fp
167654359Sroberto	)
167754359Sroberto{
1678132451Sroberto	int i;
1679132451Sroberto
168054359Sroberto	if (pcmd->nargs == 0) {
168154359Sroberto		if (havehost)
168254359Sroberto		    (void) fprintf(fp, "current host is %s\n", currenthost);
168354359Sroberto		else
168454359Sroberto		    (void) fprintf(fp, "no current host\n");
1685132451Sroberto		return;
1686132451Sroberto	}
1687132451Sroberto
1688132451Sroberto	i = 0;
1689132451Sroberto	if (pcmd->nargs == 2) {
1690132451Sroberto		if (!strcmp("-4", pcmd->argval[i].string))
1691132451Sroberto			ai_fam_templ = AF_INET;
1692132451Sroberto		else if (!strcmp("-6", pcmd->argval[i].string))
1693132451Sroberto			ai_fam_templ = AF_INET6;
1694132451Sroberto		else {
1695132451Sroberto			if (havehost)
1696132451Sroberto				(void) fprintf(fp,
1697132451Sroberto				    "current host remains %s\n", currenthost);
1698132451Sroberto			else
1699132451Sroberto				(void) fprintf(fp, "still no current host\n");
1700132451Sroberto			return;
1701132451Sroberto		}
1702132451Sroberto		i = 1;
1703132451Sroberto	}
1704132451Sroberto	if (openhost(pcmd->argval[i].string)) {
170554359Sroberto		(void) fprintf(fp, "current host set to %s\n", currenthost);
170654359Sroberto	} else {
170754359Sroberto		if (havehost)
170854359Sroberto		    (void) fprintf(fp,
170954359Sroberto				   "current host remains %s\n", currenthost);
171054359Sroberto		else
171154359Sroberto		    (void) fprintf(fp, "still no current host\n");
171254359Sroberto	}
171354359Sroberto}
171454359Sroberto
171554359Sroberto
171654359Sroberto/*
171754359Sroberto * keyid - get a keyid to use for authenticating requests
171854359Sroberto */
171954359Srobertostatic void
172054359Srobertokeyid(
172154359Sroberto	struct parse *pcmd,
172254359Sroberto	FILE *fp
172354359Sroberto	)
172454359Sroberto{
172554359Sroberto	if (pcmd->nargs == 0) {
1726182007Sroberto		if (info_auth_keyid == 0 && !keyid_entered)
172754359Sroberto		    (void) fprintf(fp, "no keyid defined\n");
1728182007Sroberto		else if (info_auth_keyid == 0 && keyid_entered)
1729182007Sroberto		    (void) fprintf(fp, "no keyid will be sent\n");
173054359Sroberto		else
173154359Sroberto		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
173254359Sroberto	} else {
173354359Sroberto		info_auth_keyid = pcmd->argval[0].uval;
1734182007Sroberto		keyid_entered = 1;
173554359Sroberto	}
173654359Sroberto}
173754359Sroberto
173854359Sroberto
173954359Sroberto/*
174054359Sroberto * keytype - get type of key to use for authenticating requests
174154359Sroberto */
174254359Srobertostatic void
174354359Srobertokeytype(
174454359Sroberto	struct parse *pcmd,
174554359Sroberto	FILE *fp
174654359Sroberto	)
174754359Sroberto{
1748290000Sglebius	const char *	digest_name;
1749290000Sglebius	size_t		digest_len;
1750290000Sglebius	int		key_type;
175154359Sroberto
1752290000Sglebius	if (!pcmd->nargs) {
1753290000Sglebius		fprintf(fp, "keytype is %s with %lu octet digests\n",
1754290000Sglebius			keytype_name(info_auth_keytype),
1755290000Sglebius			(u_long)info_auth_hashlen);
1756290000Sglebius		return;
1757290000Sglebius	}
1758290000Sglebius
1759290000Sglebius	digest_name = pcmd->argval[0].string;
1760290000Sglebius	digest_len = 0;
1761290000Sglebius	key_type = keytype_from_text(digest_name, &digest_len);
1762290000Sglebius
1763290000Sglebius	if (!key_type) {
1764290000Sglebius		fprintf(fp, "keytype must be 'md5'%s\n",
1765290000Sglebius#ifdef OPENSSL
1766290000Sglebius			" or a digest type provided by OpenSSL");
1767290000Sglebius#else
1768290000Sglebius			"");
1769290000Sglebius#endif
1770290000Sglebius		return;
1771290000Sglebius	}
1772290000Sglebius
1773290000Sglebius	info_auth_keytype = key_type;
1774290000Sglebius	info_auth_hashlen = digest_len;
177554359Sroberto}
177654359Sroberto
177754359Sroberto
177854359Sroberto/*
177954359Sroberto * passwd - get an authentication key
178054359Sroberto */
178154359Sroberto/*ARGSUSED*/
178254359Srobertostatic void
178354359Srobertopasswd(
178454359Sroberto	struct parse *pcmd,
178554359Sroberto	FILE *fp
178654359Sroberto	)
178754359Sroberto{
178854359Sroberto	char *pass;
178954359Sroberto
179054359Sroberto	if (info_auth_keyid == 0) {
179154359Sroberto		info_auth_keyid = getkeyid("Keyid: ");
179254359Sroberto		if (info_auth_keyid == 0) {
179354359Sroberto			(void)fprintf(fp, "Keyid must be defined\n");
179454359Sroberto			return;
179554359Sroberto		}
179654359Sroberto	}
1797290000Sglebius	if (pcmd->nargs >= 1)
1798290000Sglebius		pass = pcmd->argval[0].string;
1799290000Sglebius	else {
1800290000Sglebius		pass = getpass_keytype(info_auth_keytype);
1801290000Sglebius		if ('\0' == *pass) {
1802290000Sglebius			fprintf(fp, "Password unchanged\n");
1803290000Sglebius			return;
180482498Sroberto		}
180554359Sroberto	}
1806290000Sglebius	authusekey(info_auth_keyid, info_auth_keytype, (u_char *)pass);
1807290000Sglebius	authtrust(info_auth_keyid, 1);
180854359Sroberto}
180954359Sroberto
181054359Sroberto
181154359Sroberto/*
181254359Sroberto * hostnames - set the showhostnames flag
181354359Sroberto */
181454359Srobertostatic void
181554359Srobertohostnames(
181654359Sroberto	struct parse *pcmd,
181754359Sroberto	FILE *fp
181854359Sroberto	)
181954359Sroberto{
182054359Sroberto	if (pcmd->nargs == 0) {
182154359Sroberto		if (showhostnames)
182254359Sroberto		    (void) fprintf(fp, "hostnames being shown\n");
182354359Sroberto		else
182454359Sroberto		    (void) fprintf(fp, "hostnames not being shown\n");
182554359Sroberto	} else {
182654359Sroberto		if (STREQ(pcmd->argval[0].string, "yes"))
182754359Sroberto		    showhostnames = 1;
182854359Sroberto		else if (STREQ(pcmd->argval[0].string, "no"))
182954359Sroberto		    showhostnames = 0;
183054359Sroberto		else
183154359Sroberto		    (void)fprintf(stderr, "What?\n");
183254359Sroberto	}
183354359Sroberto}
183454359Sroberto
183554359Sroberto
183654359Sroberto/*
183754359Sroberto * setdebug - set/change debugging level
183854359Sroberto */
183954359Srobertostatic void
184054359Srobertosetdebug(
184154359Sroberto	struct parse *pcmd,
184254359Sroberto	FILE *fp
184354359Sroberto	)
184454359Sroberto{
184554359Sroberto	if (pcmd->nargs == 0) {
184654359Sroberto		(void) fprintf(fp, "debug level is %d\n", debug);
184754359Sroberto		return;
184854359Sroberto	} else if (STREQ(pcmd->argval[0].string, "no")) {
184954359Sroberto		debug = 0;
185054359Sroberto	} else if (STREQ(pcmd->argval[0].string, "more")) {
185154359Sroberto		debug++;
185254359Sroberto	} else if (STREQ(pcmd->argval[0].string, "less")) {
185354359Sroberto		debug--;
185454359Sroberto	} else {
185554359Sroberto		(void) fprintf(fp, "What?\n");
185654359Sroberto		return;
185754359Sroberto	}
185854359Sroberto	(void) fprintf(fp, "debug level set to %d\n", debug);
185954359Sroberto}
186054359Sroberto
186154359Sroberto
186254359Sroberto/*
186354359Sroberto * quit - stop this nonsense
186454359Sroberto */
186554359Sroberto/*ARGSUSED*/
186654359Srobertostatic void
186754359Srobertoquit(
186854359Sroberto	struct parse *pcmd,
186954359Sroberto	FILE *fp
187054359Sroberto	)
187154359Sroberto{
187254359Sroberto	if (havehost)
187354359Sroberto	    closesocket(sockfd);
187454359Sroberto	exit(0);
187554359Sroberto}
187654359Sroberto
187754359Sroberto
187854359Sroberto/*
187954359Sroberto * version - print the current version number
188054359Sroberto */
188154359Sroberto/*ARGSUSED*/
188254359Srobertostatic void
188354359Srobertoversion(
188454359Sroberto	struct parse *pcmd,
188554359Sroberto	FILE *fp
188654359Sroberto	)
188754359Sroberto{
188854359Sroberto
188954359Sroberto	(void) fprintf(fp, "%s\n", Version);
189054359Sroberto	return;
189154359Sroberto}
189254359Sroberto
189354359Sroberto
1894290000Sglebiusstatic void __attribute__((__format__(__printf__, 1, 0)))
1895290000Sglebiusvwarning(const char *fmt, va_list ap)
1896290000Sglebius{
1897290000Sglebius	int serrno = errno;
1898290000Sglebius	(void) fprintf(stderr, "%s: ", progname);
1899290000Sglebius	vfprintf(stderr, fmt, ap);
1900290000Sglebius	(void) fprintf(stderr, ": %s\n", strerror(serrno));
1901290000Sglebius}
1902290000Sglebius
190354359Sroberto/*
190454359Sroberto * warning - print a warning message
190554359Sroberto */
1906290000Sglebiusstatic void __attribute__((__format__(__printf__, 1, 2)))
190754359Srobertowarning(
190854359Sroberto	const char *fmt,
1909290000Sglebius	...
191054359Sroberto	)
191154359Sroberto{
1912290000Sglebius	va_list ap;
1913290000Sglebius	va_start(ap, fmt);
1914290000Sglebius	vwarning(fmt, ap);
1915290000Sglebius	va_end(ap);
191654359Sroberto}
191754359Sroberto
191854359Sroberto
191954359Sroberto/*
192054359Sroberto * error - print a message and exit
192154359Sroberto */
1922290000Sglebiusstatic void __attribute__((__format__(__printf__, 1, 2)))
192354359Srobertoerror(
192454359Sroberto	const char *fmt,
1925290000Sglebius	...
192654359Sroberto	)
192754359Sroberto{
1928290000Sglebius	va_list ap;
1929290000Sglebius	va_start(ap, fmt);
1930290000Sglebius	vwarning(fmt, ap);
1931290000Sglebius	va_end(ap);
193254359Sroberto	exit(1);
193354359Sroberto}
193454359Sroberto
193554359Sroberto/*
193654359Sroberto * getkeyid - prompt the user for a keyid to use
193754359Sroberto */
193854359Srobertostatic u_long
193954359Srobertogetkeyid(
194054359Sroberto	const char *keyprompt
194154359Sroberto	)
194254359Sroberto{
1943290000Sglebius	int c;
194454359Sroberto	FILE *fi;
194554359Sroberto	char pbuf[20];
1946290000Sglebius	size_t i;
1947290000Sglebius	size_t ilim;
194854359Sroberto
194954359Sroberto#ifndef SYS_WINNT
195054359Sroberto	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
195154359Sroberto#else
1952290000Sglebius	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
195354359Sroberto#endif /* SYS_WINNT */
195454359Sroberto		fi = stdin;
1955290000Sglebius	else
195654359Sroberto		setbuf(fi, (char *)NULL);
195754359Sroberto	fprintf(stderr, "%s", keyprompt); fflush(stderr);
1958290000Sglebius	for (i = 0, ilim = COUNTOF(pbuf) - 1;
1959290000Sglebius	     i < ilim && (c = getc(fi)) != '\n' && c != EOF;
1960290000Sglebius	     )
1961290000Sglebius		pbuf[i++] = (char)c;
1962290000Sglebius	pbuf[i] = '\0';
196354359Sroberto	if (fi != stdin)
1964290000Sglebius		fclose(fi);
1965290000Sglebius
1966290000Sglebius	return (u_long) atoi(pbuf);
196754359Sroberto}
1968