154359Sroberto/*
254359Sroberto * ntpdc - control and monitor your ntpd daemon
354359Sroberto */
4285612Sdelphij#include <config.h>
554359Sroberto#include <stdio.h>
6285612Sdelphij#include <stddef.h>
7182007Sroberto#include <ctype.h>
8182007Sroberto#include <signal.h>
9182007Sroberto#include <setjmp.h>
10285612Sdelphij#ifdef HAVE_UNISTD_H
11285612Sdelphij# include <unistd.h>
12285612Sdelphij#endif
13285612Sdelphij#ifdef HAVE_FCNTL_H
14285612Sdelphij# include <fcntl.h>
15285612Sdelphij#endif
16285612Sdelphij#ifdef SYS_WINNT
17285612Sdelphij# include <mswsock.h>
18285612Sdelphij#endif
19285612Sdelphij#include <isc/net.h>
20285612Sdelphij#include <isc/result.h>
21182007Sroberto
2282498Sroberto#include "ntpdc.h"
2382498Sroberto#include "ntp_select.h"
2482498Sroberto#include "ntp_stdlib.h"
25285612Sdelphij#include "ntp_assert.h"
26285612Sdelphij#include "ntp_lineedit.h"
27285612Sdelphij#ifdef OPENSSL
28285612Sdelphij#include "openssl/evp.h"
29285612Sdelphij#include "openssl/objects.h"
30285612Sdelphij#endif
31285612Sdelphij#include <ssl_applink.c>
3282498Sroberto
33285612Sdelphij#include "ntp_libopts.h"
34182007Sroberto#include "ntpdc-opts.h"
35293650Sglebius#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
62285612Sdelphijstatic	int	info_auth_keytype = NID_md5;	/* MD5 */
63285612Sdelphijstatic	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
71285612Sdelphijint		ntpdcmain	(int,	char **);
7254359Sroberto/*
7354359Sroberto * Built in command handler declarations
7454359Sroberto */
75285612Sdelphijstatic	int	openhost	(const char *);
76285612Sdelphijstatic	int	sendpkt		(void *, size_t);
77285612Sdelphijstatic	void	growpktdata	(void);
78293650Sglebiusstatic	int	getresponse	(int, int, size_t *, size_t *, const char **, size_t);
79293650Sglebiusstatic	int	sendrequest	(int, int, int, size_t, size_t, const char *);
80285612Sdelphijstatic	void	getcmds		(void);
81285612Sdelphijstatic	RETSIGTYPE abortcmd	(int);
82285612Sdelphijstatic	void	docmd		(const char *);
83285612Sdelphijstatic	void	tokenize	(const char *, char **, int *);
84285612Sdelphijstatic	int	findcmd		(char *, struct xcmd *, struct xcmd *, struct xcmd **);
85285612Sdelphijstatic	int	getarg		(char *, int, arg_v *);
86285612Sdelphijstatic	int	getnetnum	(const char *, sockaddr_u *, char *, int);
87285612Sdelphijstatic	void	help		(struct parse *, FILE *);
88285612Sdelphijstatic	int	helpsort	(const void *, const void *);
89285612Sdelphijstatic	void	printusage	(struct xcmd *, FILE *);
90285612Sdelphijstatic	void	timeout		(struct parse *, FILE *);
91285612Sdelphijstatic	void	my_delay	(struct parse *, FILE *);
92285612Sdelphijstatic	void	host		(struct parse *, FILE *);
93285612Sdelphijstatic	void	keyid		(struct parse *, FILE *);
94285612Sdelphijstatic	void	keytype		(struct parse *, FILE *);
95285612Sdelphijstatic	void	passwd		(struct parse *, FILE *);
96285612Sdelphijstatic	void	hostnames	(struct parse *, FILE *);
97285612Sdelphijstatic	void	setdebug	(struct parse *, FILE *);
98285612Sdelphijstatic	void	quit		(struct parse *, FILE *);
99285612Sdelphijstatic	void	version		(struct parse *, FILE *);
100285612Sdelphijstatic	void	warning		(const char *, ...)
101285612Sdelphij    __attribute__((__format__(__printf__, 1, 2)));
102285612Sdelphijstatic	void	error		(const char *, ...)
103285612Sdelphij    __attribute__((__format__(__printf__, 1, 2)));
104285612Sdelphijstatic	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 */
159285612Sdelphij#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 */
173285612Sdelphijstatic	struct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
174285612Sdelphijstatic	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/*
229338531Sdelphij * Jump buffer for longjumping back to the command level.
230338531Sdelphij *
231338531Sdelphij * See ntpq/ntpq.c for an explanation why 'sig{set,long}jmp()' is used
232338531Sdelphij * when available.
23354359Sroberto */
234338531Sdelphij#if HAVE_DECL_SIGSETJMP && HAVE_DECL_SIGLONGJMP
235338531Sdelphij# define JMP_BUF	sigjmp_buf
236338531Sdelphij# define SETJMP(x)	sigsetjmp((x), 1)
237338531Sdelphij# define LONGJMP(x, v)	siglongjmp((x),(v))
238338531Sdelphij#else
239338531Sdelphij# define JMP_BUF	jmp_buf
240338531Sdelphij# define SETJMP(x)	setjmp((x))
241338531Sdelphij# define LONGJMP(x, v)	longjmp((x),(v))
242338531Sdelphij#endif
243338531Sdelphijstatic	JMP_BUF		interrupt_buf;
244338531Sdelphijstatic	volatile int	jump = 0;
24554359Sroberto
24654359Sroberto/*
24754359Sroberto * Pointer to current output unit
24854359Sroberto */
249338531Sdelphijstatic	FILE *current_output = NULL;
25054359Sroberto
25154359Sroberto/*
25254359Sroberto * Command table imported from ntpdc_ops.c
25354359Sroberto */
25454359Srobertoextern struct xcmd opcmds[];
25554359Sroberto
256289997Sglebiuschar const *progname;
25754359Sroberto
25854359Sroberto#ifdef NO_MAIN_ALLOWED
25954359SrobertoCALL(ntpdc,"ntpdc",ntpdcmain);
26054359Sroberto#else
26154359Srobertoint
26254359Srobertomain(
26354359Sroberto	int argc,
26454359Sroberto	char *argv[]
26554359Sroberto	)
26654359Sroberto{
26754359Sroberto	return ntpdcmain(argc, argv);
26854359Sroberto}
26954359Sroberto#endif
27054359Sroberto
27154359Sroberto#ifdef SYS_VXWORKS
27254359Srobertovoid clear_globals(void)
27354359Sroberto{
27454359Sroberto    showhostnames = 0;              /* show host names by default */
27554359Sroberto    havehost = 0;                   /* set to 1 when host open */
27654359Sroberto    numcmds = 0;
27754359Sroberto    numhosts = 0;
27854359Sroberto}
27954359Sroberto#endif
28054359Sroberto
28154359Sroberto/*
28254359Sroberto * main - parse arguments and handle options
28354359Sroberto */
28454359Srobertoint
28554359Srobertontpdcmain(
28654359Sroberto	int argc,
28754359Sroberto	char *argv[]
28854359Sroberto	)
28954359Sroberto{
29054359Sroberto	delay_time.l_ui = 0;
29154359Sroberto	delay_time.l_uf = DEFDELAY;
29254359Sroberto
29354359Sroberto#ifdef SYS_VXWORKS
29454359Sroberto	clear_globals();
29554359Sroberto	taskPrioritySet(taskIdSelf(), 100 );
29654359Sroberto#endif
29754359Sroberto
298285612Sdelphij	init_lib();	/* sets up ipv4_works, ipv6_works */
299285612Sdelphij	ssl_applink();
300285612Sdelphij	init_auth();
301132451Sroberto
302285612Sdelphij	/* Check to see if we have IPv6. Otherwise default to IPv4 */
303285612Sdelphij	if (!ipv6_works)
304132451Sroberto		ai_fam_default = AF_INET;
305132451Sroberto
30654359Sroberto	progname = argv[0];
307182007Sroberto
308182007Sroberto	{
309285612Sdelphij		int optct = ntpOptionProcess(&ntpdcOptions, argc, argv);
310182007Sroberto		argc -= optct;
311182007Sroberto		argv += optct;
312182007Sroberto	}
313182007Sroberto
314285612Sdelphij	if (HAVE_OPT(IPV4))
315182007Sroberto		ai_fam_templ = AF_INET;
316285612Sdelphij	else if (HAVE_OPT(IPV6))
317182007Sroberto		ai_fam_templ = AF_INET6;
318285612Sdelphij	else
319182007Sroberto		ai_fam_templ = ai_fam_default;
320182007Sroberto
321182007Sroberto	if (HAVE_OPT(COMMAND)) {
322182007Sroberto		int		cmdct = STACKCT_OPT( COMMAND );
323182007Sroberto		const char**	cmds  = STACKLST_OPT( COMMAND );
324182007Sroberto
325182007Sroberto		while (cmdct-- > 0) {
326182007Sroberto			ADDCMD(*cmds++);
327182007Sroberto		}
328182007Sroberto	}
329182007Sroberto
330285612Sdelphij	debug = OPT_VALUE_SET_DEBUG_LEVEL;
331182007Sroberto
332182007Sroberto	if (HAVE_OPT(INTERACTIVE)) {
333182007Sroberto		interactive = 1;
334182007Sroberto	}
335182007Sroberto
336182007Sroberto	if (HAVE_OPT(NUMERIC)) {
337182007Sroberto		showhostnames = 0;
338182007Sroberto	}
339182007Sroberto
340182007Sroberto	if (HAVE_OPT(LISTPEERS)) {
341182007Sroberto		ADDCMD("listpeers");
342182007Sroberto	}
343182007Sroberto
344182007Sroberto	if (HAVE_OPT(PEERS)) {
345182007Sroberto		ADDCMD("peers");
346182007Sroberto	}
347182007Sroberto
348182007Sroberto	if (HAVE_OPT(SHOWPEERS)) {
349182007Sroberto		ADDCMD("dmpeers");
350182007Sroberto	}
351182007Sroberto
352182007Sroberto	if (ntp_optind == argc) {
353182007Sroberto		ADDHOST(DEFHOST);
354182007Sroberto	} else {
355182007Sroberto		for (; ntp_optind < argc; ntp_optind++)
356182007Sroberto		    ADDHOST(argv[ntp_optind]);
357182007Sroberto	}
358182007Sroberto
359182007Sroberto	if (numcmds == 0 && interactive == 0
360182007Sroberto	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
361182007Sroberto		interactive = 1;
362182007Sroberto	}
363182007Sroberto
36454359Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
36554359Sroberto	if (interactive)
366338531Sdelphij		(void) signal_no_reset(SIGINT, abortcmd);
36754359Sroberto#endif /* SYS_WINNT */
36854359Sroberto
36954359Sroberto	/*
37054359Sroberto	 * Initialize the packet data buffer
37154359Sroberto	 */
37254359Sroberto	pktdatasize = INITDATASIZE;
373285612Sdelphij	pktdata = emalloc(INITDATASIZE);
37454359Sroberto
37554359Sroberto	if (numcmds == 0) {
37654359Sroberto		(void) openhost(chosts[0]);
37754359Sroberto		getcmds();
37854359Sroberto	} else {
37954359Sroberto		int ihost;
38054359Sroberto		int icmd;
38154359Sroberto
38254359Sroberto		for (ihost = 0; ihost < numhosts; ihost++) {
38354359Sroberto			if (openhost(chosts[ihost]))
38454359Sroberto			    for (icmd = 0; icmd < numcmds; icmd++) {
38554359Sroberto				    if (numhosts > 1)
38654359Sroberto					printf ("--- %s ---\n",chosts[ihost]);
38754359Sroberto				    docmd(ccmds[icmd]);
38854359Sroberto			    }
38954359Sroberto		}
39054359Sroberto	}
39154359Sroberto#ifdef SYS_WINNT
39254359Sroberto	WSACleanup();
39354359Sroberto#endif
39454359Sroberto	return(0);
39554359Sroberto} /* main end */
39654359Sroberto
39754359Sroberto
39854359Sroberto/*
39954359Sroberto * openhost - open a socket to a host
40054359Sroberto */
40154359Srobertostatic int
40254359Srobertoopenhost(
40354359Sroberto	const char *hname
40454359Sroberto	)
40554359Sroberto{
40654359Sroberto	char temphost[LENHOSTNAME];
407338531Sdelphij	int a_info;
408132451Sroberto	struct addrinfo hints, *ai = NULL;
409285612Sdelphij	sockaddr_u addr;
410285612Sdelphij	size_t octets;
411338531Sdelphij	const char *cp;
412132451Sroberto	char name[LENHOSTNAME];
413132451Sroberto	char service[5];
41454359Sroberto
415132451Sroberto	/*
416132451Sroberto	 * We need to get by the [] if they were entered
417132451Sroberto	 */
418338531Sdelphij	if (*hname == '[') {
419338531Sdelphij		cp = strchr(hname + 1, ']');
420338531Sdelphij		if (!cp || (octets = (size_t)(cp - hname) - 1) >= sizeof(name)) {
421338531Sdelphij			errno = EINVAL;
422338531Sdelphij			warning("%s", "bad hostname/address");
423285612Sdelphij			return 0;
424285612Sdelphij		}
425338531Sdelphij		memcpy(name, hname + 1, octets);
426338531Sdelphij		name[octets] = '\0';
427338531Sdelphij		hname = name;
428338531Sdelphij	}
429132451Sroberto
430132451Sroberto	/*
431132451Sroberto	 * First try to resolve it as an ip address and if that fails,
432132451Sroberto	 * do a fullblown (dns) lookup. That way we only use the dns
433132451Sroberto	 * when it is needed and work around some implementations that
434132451Sroberto	 * will return an "IPv4-mapped IPv6 address" address if you
435132451Sroberto	 * give it an IPv4 address to lookup.
436132451Sroberto	 */
437285612Sdelphij	strlcpy(service, "ntp", sizeof(service));
438285612Sdelphij	ZERO(hints);
439132451Sroberto	hints.ai_family = ai_fam_templ;
440132451Sroberto	hints.ai_protocol = IPPROTO_UDP;
441132451Sroberto	hints.ai_socktype = SOCK_DGRAM;
442285612Sdelphij	hints.ai_flags = Z_AI_NUMERICHOST;
443132451Sroberto
444132451Sroberto	a_info = getaddrinfo(hname, service, &hints, &ai);
445182007Sroberto	if (a_info == EAI_NONAME
446182007Sroberto#ifdef EAI_NODATA
447182007Sroberto	    || a_info == EAI_NODATA
448182007Sroberto#endif
449182007Sroberto	   ) {
450132451Sroberto		hints.ai_flags = AI_CANONNAME;
451132451Sroberto#ifdef AI_ADDRCONFIG
452132451Sroberto		hints.ai_flags |= AI_ADDRCONFIG;
453132451Sroberto#endif
454132451Sroberto		a_info = getaddrinfo(hname, service, &hints, &ai);
45554359Sroberto	}
456132451Sroberto	/* Some older implementations don't like AI_ADDRCONFIG. */
457132451Sroberto	if (a_info == EAI_BADFLAGS) {
458132451Sroberto		hints.ai_flags = AI_CANONNAME;
459132451Sroberto		a_info = getaddrinfo(hname, service, &hints, &ai);
460132451Sroberto	}
461132451Sroberto	if (a_info != 0) {
462285612Sdelphij		fprintf(stderr, "%s\n", gai_strerror(a_info));
463182007Sroberto		if (ai != NULL)
464182007Sroberto			freeaddrinfo(ai);
465132451Sroberto		return 0;
466132451Sroberto	}
46754359Sroberto
468285612Sdelphij	/*
469285612Sdelphij	 * getaddrinfo() has returned without error so ai should not
470285612Sdelphij	 * be NULL.
471285612Sdelphij	 */
472285612Sdelphij	INSIST(ai != NULL);
473285612Sdelphij	ZERO(addr);
474285612Sdelphij	octets = min(sizeof(addr), ai->ai_addrlen);
475285612Sdelphij	memcpy(&addr, ai->ai_addr, octets);
476132451Sroberto
477285612Sdelphij	if (ai->ai_canonname == NULL)
478285612Sdelphij		strlcpy(temphost, stoa(&addr), sizeof(temphost));
479285612Sdelphij	else
480285612Sdelphij		strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
481285612Sdelphij
48254359Sroberto	if (debug > 2)
483285612Sdelphij		printf("Opening host %s\n", temphost);
48454359Sroberto
48554359Sroberto	if (havehost == 1) {
48654359Sroberto		if (debug > 2)
487285612Sdelphij			printf("Closing old host %s\n", currenthost);
488285612Sdelphij		closesocket(sockfd);
48954359Sroberto		havehost = 0;
49054359Sroberto	}
491285612Sdelphij	strlcpy(currenthost, temphost, sizeof(currenthost));
492132451Sroberto
493132451Sroberto	/* port maps to the same in both families */
494285612Sdelphij	s_port = NSRCPORT(&addr);;
495132451Sroberto#ifdef SYS_VXWORKS
496132451Sroberto	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
497132451Sroberto	if (ai->ai_family == AF_INET)
498132451Sroberto		*(struct sockaddr_in *)&hostaddr=
499132451Sroberto			*((struct sockaddr_in *)ai->ai_addr);
500132451Sroberto	else
501132451Sroberto		*(struct sockaddr_in6 *)&hostaddr=
502132451Sroberto			*((struct sockaddr_in6 *)ai->ai_addr);
503132451Sroberto#endif /* SYS_VXWORKS */
50454359Sroberto
50554359Sroberto#ifdef SYS_WINNT
50654359Sroberto	{
50754359Sroberto		int optionValue = SO_SYNCHRONOUS_NONALERT;
50854359Sroberto		int err;
509182007Sroberto
510330141Sdelphij		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (void *)&optionValue, sizeof(optionValue));
51154359Sroberto		if (err != NO_ERROR) {
51254359Sroberto			(void) fprintf(stderr, "cannot open nonoverlapped sockets\n");
51354359Sroberto			exit(1);
51454359Sroberto		}
51554359Sroberto	}
516285612Sdelphij#endif /* SYS_WINNT */
517132451Sroberto
518132451Sroberto	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
51954359Sroberto	if (sockfd == INVALID_SOCKET) {
520285612Sdelphij		error("socket");
52154359Sroberto		exit(-1);
52254359Sroberto	}
52354359Sroberto
52454359Sroberto#ifdef NEED_RCVBUF_SLOP
52554359Sroberto# ifdef SO_RCVBUF
52654359Sroberto	{
52754359Sroberto		int rbufsize = INITDATASIZE + 2048; /* 2K for slop */
52854359Sroberto
52954359Sroberto		if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
530330141Sdelphij			       (void *)&rbufsize, sizeof(int)) == -1)
531285612Sdelphij		    error("setsockopt");
53254359Sroberto	}
53354359Sroberto# endif
53454359Sroberto#endif
53554359Sroberto
536132451Sroberto#ifdef SYS_VXWORKS
537132451Sroberto	if (connect(sockfd, (struct sockaddr *)&hostaddr,
538293650Sglebius		    sizeof(hostaddr)) == -1)
539132451Sroberto#else
540293650Sglebius	if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) == -1)
541132451Sroberto#endif /* SYS_VXWORKS */
542293650Sglebius	{
543285612Sdelphij		error("connect");
544285612Sdelphij		exit(-1);
545285612Sdelphij	}
546285612Sdelphij
547285612Sdelphij	freeaddrinfo(ai);
54854359Sroberto	havehost = 1;
549106163Sroberto	req_pkt_size = REQ_LEN_NOMAC;
550132451Sroberto	impl_ver = IMPL_XNTPD;
55154359Sroberto	return 1;
55254359Sroberto}
55354359Sroberto
55454359Sroberto
55554359Sroberto/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
55654359Sroberto/*
55754359Sroberto * sendpkt - send a packet to the remote host
55854359Sroberto */
55954359Srobertostatic int
56054359Srobertosendpkt(
561285612Sdelphij	void *	xdata,
562285612Sdelphij	size_t	xdatalen
56354359Sroberto	)
56454359Sroberto{
565285612Sdelphij	if (send(sockfd, xdata, xdatalen, 0) == -1) {
566285612Sdelphij		warning("write to %s failed", currenthost);
56754359Sroberto		return -1;
56854359Sroberto	}
56954359Sroberto
57054359Sroberto	return 0;
57154359Sroberto}
57254359Sroberto
57354359Sroberto
57454359Sroberto/*
57554359Sroberto * growpktdata - grow the packet data area
57654359Sroberto */
57754359Srobertostatic void
57854359Srobertogrowpktdata(void)
57954359Sroberto{
580285612Sdelphij	size_t priorsz;
581285612Sdelphij
582285612Sdelphij	priorsz = (size_t)pktdatasize;
58354359Sroberto	pktdatasize += INCDATASIZE;
584285612Sdelphij	pktdata = erealloc_zero(pktdata, (size_t)pktdatasize, priorsz);
58554359Sroberto}
58654359Sroberto
58754359Sroberto
58854359Sroberto/*
58954359Sroberto * getresponse - get a (series of) response packet(s) and return the data
59054359Sroberto */
59154359Srobertostatic int
59254359Srobertogetresponse(
59354359Sroberto	int implcode,
59454359Sroberto	int reqcode,
595293650Sglebius	size_t *ritems,
596293650Sglebius	size_t *rsize,
597293650Sglebius	const char **rdata,
598293650Sglebius	size_t esize
59954359Sroberto	)
60054359Sroberto{
60154359Sroberto	struct resp_pkt rpkt;
602285612Sdelphij	struct sock_timeval tvo;
603293650Sglebius	size_t items;
604293650Sglebius	size_t i;
605293650Sglebius	size_t size;
606293650Sglebius	size_t datasize;
60754359Sroberto	char *datap;
608132451Sroberto	char *tmp_data;
60954359Sroberto	char haveseq[MAXSEQ+1];
61054359Sroberto	int firstpkt;
61154359Sroberto	int lastseq;
61254359Sroberto	int numrecv;
61354359Sroberto	int seq;
61454359Sroberto	fd_set fds;
615285612Sdelphij	ssize_t n;
616294569Sdelphij	int pad;
617294569Sdelphij	/* absolute timeout checks. Not 'time_t' by intention! */
618294569Sdelphij	uint32_t tobase;	/* base value for timeout */
619294569Sdelphij	uint32_t tospan;	/* timeout span (max delay) */
620294569Sdelphij	uint32_t todiff;	/* current delay */
62154359Sroberto
62254359Sroberto	/*
62354359Sroberto	 * This is pretty tricky.  We may get between 1 and many packets
62454359Sroberto	 * back in response to the request.  We peel the data out of
62554359Sroberto	 * each packet and collect it in one long block.  When the last
62654359Sroberto	 * packet in the sequence is received we'll know how many we
62754359Sroberto	 * should have had.  Note we use one long time out, should reconsider.
62854359Sroberto	 */
62954359Sroberto	*ritems = 0;
63054359Sroberto	*rsize = 0;
63154359Sroberto	firstpkt = 1;
63254359Sroberto	numrecv = 0;
63354359Sroberto	*rdata = datap = pktdata;
63454359Sroberto	lastseq = 999;	/* too big to be a sequence number */
635285612Sdelphij	ZERO(haveseq);
63654359Sroberto	FD_ZERO(&fds);
637294569Sdelphij	tobase = (uint32_t)time(NULL);
63854359Sroberto
63954359Sroberto    again:
64054359Sroberto	if (firstpkt)
641285612Sdelphij		tvo = tvout;
64254359Sroberto	else
643285612Sdelphij		tvo = tvsout;
644294569Sdelphij	tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
64554359Sroberto
64654359Sroberto	FD_SET(sockfd, &fds);
647293650Sglebius	n = select(sockfd+1, &fds, NULL, NULL, &tvo);
64854359Sroberto	if (n == -1) {
649285612Sdelphij		warning("select fails");
65054359Sroberto		return -1;
65154359Sroberto	}
652294569Sdelphij
653294569Sdelphij	/*
654294569Sdelphij	 * Check if this is already too late. Trash the data and fake a
655294569Sdelphij	 * timeout if this is so.
656294569Sdelphij	 */
657294569Sdelphij	todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
658294569Sdelphij	if ((n > 0) && (todiff > tospan)) {
659294569Sdelphij		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
660316069Sdelphij		n -= n; /* faked timeout return from 'select()'*/
661294569Sdelphij	}
662294569Sdelphij
66354359Sroberto	if (n == 0) {
66454359Sroberto		/*
66554359Sroberto		 * Timed out.  Return what we have
66654359Sroberto		 */
66754359Sroberto		if (firstpkt) {
66854359Sroberto			(void) fprintf(stderr,
669293650Sglebius				       "%s: timed out, nothing received\n",
670293650Sglebius				       currenthost);
67154359Sroberto			return ERR_TIMEOUT;
67254359Sroberto		} else {
67354359Sroberto			(void) fprintf(stderr,
67454359Sroberto				       "%s: timed out with incomplete data\n",
67554359Sroberto				       currenthost);
67654359Sroberto			if (debug) {
67754359Sroberto				printf("Received sequence numbers");
67854359Sroberto				for (n = 0; n <= MAXSEQ; n++)
67954359Sroberto				    if (haveseq[n])
680301256Sdelphij					printf(" %zd,", (size_t)n);
68154359Sroberto				if (lastseq != 999)
68254359Sroberto				    printf(" last frame received\n");
68354359Sroberto				else
68454359Sroberto				    printf(" last frame not received\n");
68554359Sroberto			}
68654359Sroberto			return ERR_INCOMPLETE;
68754359Sroberto		}
68854359Sroberto	}
68954359Sroberto
69054359Sroberto	n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
69154359Sroberto	if (n == -1) {
692285612Sdelphij		warning("read");
69354359Sroberto		return -1;
69454359Sroberto	}
69554359Sroberto
69654359Sroberto
69754359Sroberto	/*
69854359Sroberto	 * Check for format errors.  Bug proofing.
69954359Sroberto	 */
700285612Sdelphij	if (n < (ssize_t)RESP_HEADER_SIZE) {
70154359Sroberto		if (debug)
702301256Sdelphij			printf("Short (%zd byte) packet received\n", (size_t)n);
70354359Sroberto		goto again;
70454359Sroberto	}
70554359Sroberto	if (INFO_VERSION(rpkt.rm_vn_mode) > NTP_VERSION ||
70654359Sroberto	    INFO_VERSION(rpkt.rm_vn_mode) < NTP_OLDVERSION) {
70754359Sroberto		if (debug)
708285612Sdelphij			printf("Packet received with version %d\n",
709285612Sdelphij			       INFO_VERSION(rpkt.rm_vn_mode));
71054359Sroberto		goto again;
71154359Sroberto	}
71254359Sroberto	if (INFO_MODE(rpkt.rm_vn_mode) != MODE_PRIVATE) {
71354359Sroberto		if (debug)
714285612Sdelphij			printf("Packet received with mode %d\n",
715285612Sdelphij			       INFO_MODE(rpkt.rm_vn_mode));
71654359Sroberto		goto again;
71754359Sroberto	}
71854359Sroberto	if (INFO_IS_AUTH(rpkt.auth_seq)) {
71954359Sroberto		if (debug)
720285612Sdelphij			printf("Encrypted packet received\n");
72154359Sroberto		goto again;
72254359Sroberto	}
72354359Sroberto	if (!ISRESPONSE(rpkt.rm_vn_mode)) {
72454359Sroberto		if (debug)
725285612Sdelphij			printf("Received request packet, wanted response\n");
72654359Sroberto		goto again;
72754359Sroberto	}
72854359Sroberto	if (INFO_MBZ(rpkt.mbz_itemsize) != 0) {
72954359Sroberto		if (debug)
730285612Sdelphij			printf("Received packet with nonzero MBZ field!\n");
73154359Sroberto		goto again;
73254359Sroberto	}
73354359Sroberto
73454359Sroberto	/*
73554359Sroberto	 * Check implementation/request.  Could be old data getting to us.
73654359Sroberto	 */
73754359Sroberto	if (rpkt.implementation != implcode || rpkt.request != reqcode) {
73854359Sroberto		if (debug)
739285612Sdelphij			printf(
74054359Sroberto			    "Received implementation/request of %d/%d, wanted %d/%d",
74154359Sroberto			    rpkt.implementation, rpkt.request,
74254359Sroberto			    implcode, reqcode);
74354359Sroberto		goto again;
74454359Sroberto	}
74554359Sroberto
74654359Sroberto	/*
74754359Sroberto	 * Check the error code.  If non-zero, return it.
74854359Sroberto	 */
74954359Sroberto	if (INFO_ERR(rpkt.err_nitems) != INFO_OKAY) {
75054359Sroberto		if (debug && ISMORE(rpkt.rm_vn_mode)) {
75154359Sroberto			printf("Error code %d received on not-final packet\n",
75254359Sroberto			       INFO_ERR(rpkt.err_nitems));
75354359Sroberto		}
75454359Sroberto		return (int)INFO_ERR(rpkt.err_nitems);
75554359Sroberto	}
75654359Sroberto
75754359Sroberto	/*
75854359Sroberto	 * Collect items and size.  Make sure they make sense.
75954359Sroberto	 */
76054359Sroberto	items = INFO_NITEMS(rpkt.err_nitems);
76154359Sroberto	size = INFO_ITEMSIZE(rpkt.mbz_itemsize);
762132451Sroberto	if (esize > size)
763132451Sroberto		pad = esize - size;
764132451Sroberto	else
765132451Sroberto		pad = 0;
766285612Sdelphij	datasize = items * size;
767285612Sdelphij	if ((size_t)datasize > (n-RESP_HEADER_SIZE)) {
76854359Sroberto		if (debug)
76954359Sroberto		    printf(
770293650Sglebius			    "Received items %zu, size %zu (total %zu), data in packet is %zu\n",
77154359Sroberto			    items, size, datasize, n-RESP_HEADER_SIZE);
77254359Sroberto		goto again;
77354359Sroberto	}
77454359Sroberto
77554359Sroberto	/*
77654359Sroberto	 * If this isn't our first packet, make sure the size matches
77754359Sroberto	 * the other ones.
77854359Sroberto	 */
779285612Sdelphij	if (!firstpkt && size != *rsize) {
78054359Sroberto		if (debug)
781293650Sglebius		    printf("Received itemsize %zu, previous %zu\n",
78254359Sroberto			   size, *rsize);
78354359Sroberto		goto again;
78454359Sroberto	}
78554359Sroberto	/*
786132451Sroberto	 * If we've received this before, +toss it
78754359Sroberto	 */
78854359Sroberto	seq = INFO_SEQ(rpkt.auth_seq);
78954359Sroberto	if (haveseq[seq]) {
79054359Sroberto		if (debug)
79154359Sroberto		    printf("Received duplicate sequence number %d\n", seq);
79254359Sroberto		goto again;
79354359Sroberto	}
79454359Sroberto	haveseq[seq] = 1;
79554359Sroberto
79654359Sroberto	/*
79754359Sroberto	 * If this is the last in the sequence, record that.
79854359Sroberto	 */
79954359Sroberto	if (!ISMORE(rpkt.rm_vn_mode)) {
80054359Sroberto		if (lastseq != 999) {
80154359Sroberto			printf("Received second end sequence packet\n");
80254359Sroberto			goto again;
80354359Sroberto		}
80454359Sroberto		lastseq = seq;
80554359Sroberto	}
80654359Sroberto
80754359Sroberto	/*
808294569Sdelphij	 * So far, so good.  Copy this data into the output array. Bump
809294569Sdelphij	 * the timeout base, in case we expect more data.
81054359Sroberto	 */
811294569Sdelphij	tobase = (uint32_t)time(NULL);
812132451Sroberto	if ((datap + datasize + (pad * items)) > (pktdata + pktdatasize)) {
813293650Sglebius		size_t offset = datap - pktdata;
81454359Sroberto		growpktdata();
815285612Sdelphij		*rdata = pktdata; /* might have been realloced ! */
81654359Sroberto		datap = pktdata + offset;
81754359Sroberto	}
818132451Sroberto	/*
819132451Sroberto	 * We now move the pointer along according to size and number of
820132451Sroberto	 * items.  This is so we can play nice with older implementations
821132451Sroberto	 */
822132451Sroberto
823285612Sdelphij	tmp_data = rpkt.u.data;
824285612Sdelphij	for (i = 0; i < items; i++) {
825285612Sdelphij		memcpy(datap, tmp_data, (unsigned)size);
826132451Sroberto		tmp_data += size;
827285612Sdelphij		zero_mem(datap + size, pad);
828132451Sroberto		datap += size + pad;
829132451Sroberto	}
830132451Sroberto
83154359Sroberto	if (firstpkt) {
83254359Sroberto		firstpkt = 0;
833132451Sroberto		*rsize = size + pad;
83454359Sroberto	}
83554359Sroberto	*ritems += items;
83654359Sroberto
83754359Sroberto	/*
83854359Sroberto	 * Finally, check the count of received packets.  If we've got them
83954359Sroberto	 * all, return
84054359Sroberto	 */
84154359Sroberto	++numrecv;
84254359Sroberto	if (numrecv <= lastseq)
843285612Sdelphij		goto again;
84454359Sroberto	return INFO_OKAY;
84554359Sroberto}
84654359Sroberto
84754359Sroberto
84854359Sroberto/*
84954359Sroberto * sendrequest - format and send a request packet
850285612Sdelphij *
851285612Sdelphij * Historically, ntpdc has used a fixed-size request packet regardless
852285612Sdelphij * of the actual payload size.  When authenticating, the timestamp, key
853285612Sdelphij * ID, and digest have been placed just before the end of the packet.
854285612Sdelphij * With the introduction in late 2009 of support for authenticated
855285612Sdelphij * ntpdc requests using larger 20-octet digests (vs. 16 for MD5), we
856285612Sdelphij * come up four bytes short.
857285612Sdelphij *
858285612Sdelphij * To maintain interop while allowing for larger digests, the behavior
859285612Sdelphij * is unchanged when using 16-octet digests.  For larger digests, the
860285612Sdelphij * timestamp, key ID, and digest are placed immediately following the
861285612Sdelphij * request payload, with the overall packet size variable.  ntpd can
862285612Sdelphij * distinguish 16-octet digests by the overall request size being
863285612Sdelphij * REQ_LEN_NOMAC + 4 + 16 with the auth bit enabled.  When using a
864285612Sdelphij * longer digest, that request size should be avoided.
865285612Sdelphij *
866285612Sdelphij * With the form used with 20-octet and larger digests, the timestamp,
867285612Sdelphij * key ID, and digest are located by ntpd relative to the start of the
868285612Sdelphij * packet, and the size of the digest is then implied by the packet
869285612Sdelphij * size.
87054359Sroberto */
87154359Srobertostatic int
87254359Srobertosendrequest(
87354359Sroberto	int implcode,
87454359Sroberto	int reqcode,
87554359Sroberto	int auth,
876293650Sglebius	size_t qitems,
877285612Sdelphij	size_t qsize,
878293650Sglebius	const char *qdata
87954359Sroberto	)
88054359Sroberto{
88154359Sroberto	struct req_pkt qpkt;
882285612Sdelphij	size_t	datasize;
883285612Sdelphij	size_t	reqsize;
884285612Sdelphij	u_long	key_id;
885285612Sdelphij	l_fp	ts;
886285612Sdelphij	l_fp *	ptstamp;
887293650Sglebius	size_t	maclen;
888285612Sdelphij	char *	pass;
88954359Sroberto
890285612Sdelphij	ZERO(qpkt);
89154359Sroberto	qpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
89254359Sroberto	qpkt.implementation = (u_char)implcode;
89354359Sroberto	qpkt.request = (u_char)reqcode;
89454359Sroberto
89554359Sroberto	datasize = qitems * qsize;
896285612Sdelphij	if (datasize && qdata != NULL) {
897285612Sdelphij		memcpy(qpkt.u.data, qdata, datasize);
89854359Sroberto		qpkt.err_nitems = ERR_NITEMS(0, qitems);
89954359Sroberto		qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);
90054359Sroberto	} else {
90154359Sroberto		qpkt.err_nitems = ERR_NITEMS(0, 0);
902182007Sroberto		qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);  /* allow for optional first item */
90354359Sroberto	}
90454359Sroberto
905182007Sroberto	if (!auth || (keyid_entered && info_auth_keyid == 0)) {
90654359Sroberto		qpkt.auth_seq = AUTH_SEQ(0, 0);
907285612Sdelphij		return sendpkt(&qpkt, req_pkt_size);
908285612Sdelphij	}
90954359Sroberto
910285612Sdelphij	if (info_auth_keyid == 0) {
911285612Sdelphij		key_id = getkeyid("Keyid: ");
912285612Sdelphij		if (!key_id) {
913285612Sdelphij			fprintf(stderr, "Invalid key identifier\n");
914285612Sdelphij			return 1;
91554359Sroberto		}
916285612Sdelphij		info_auth_keyid = key_id;
917285612Sdelphij	}
918285612Sdelphij	if (!authistrusted(info_auth_keyid)) {
919285612Sdelphij		pass = getpass_keytype(info_auth_keytype);
920285612Sdelphij		if ('\0' == pass[0]) {
921285612Sdelphij			fprintf(stderr, "Invalid password\n");
922285612Sdelphij			return 1;
92354359Sroberto		}
924285612Sdelphij		authusekey(info_auth_keyid, info_auth_keytype,
925285612Sdelphij			   (u_char *)pass);
92654359Sroberto		authtrust(info_auth_keyid, 1);
92754359Sroberto	}
928285612Sdelphij	qpkt.auth_seq = AUTH_SEQ(1, 0);
929285612Sdelphij	if (info_auth_hashlen > 16) {
930285612Sdelphij		/*
931285612Sdelphij		 * Only ntpd which expects REQ_LEN_NOMAC plus maclen
932285612Sdelphij		 * octets in an authenticated request using a 16 octet
933285612Sdelphij		 * digest (that is, a newer ntpd) will handle digests
934285612Sdelphij		 * larger than 16 octets, so for longer digests, do
935285612Sdelphij		 * not attempt to shorten the requests for downlevel
936285612Sdelphij		 * ntpd compatibility.
937285612Sdelphij		 */
938285612Sdelphij		if (REQ_LEN_NOMAC != req_pkt_size)
939285612Sdelphij			return 1;
940285612Sdelphij		reqsize = REQ_LEN_HDR + datasize + sizeof(*ptstamp);
941285612Sdelphij		/* align to 32 bits */
942285612Sdelphij		reqsize = (reqsize + 3) & ~3;
943285612Sdelphij	} else
944285612Sdelphij		reqsize = req_pkt_size;
945285612Sdelphij	ptstamp = (void *)((char *)&qpkt + reqsize);
946285612Sdelphij	ptstamp--;
947285612Sdelphij	get_systime(&ts);
948285612Sdelphij	L_ADD(&ts, &delay_time);
949285612Sdelphij	HTONL_FP(&ts, ptstamp);
950293650Sglebius	maclen = authencrypt(
951293650Sglebius		info_auth_keyid, (void *)&qpkt, size2int_chk(reqsize));
952285612Sdelphij	if (!maclen) {
953285612Sdelphij		fprintf(stderr, "Key not found\n");
954285612Sdelphij		return 1;
955338531Sdelphij	} else if (maclen != (size_t)(info_auth_hashlen + sizeof(keyid_t))) {
956285612Sdelphij		fprintf(stderr,
957293650Sglebius			"%zu octet MAC, %zu expected with %zu octet digest\n",
958285612Sdelphij			maclen, (info_auth_hashlen + sizeof(keyid_t)),
959285612Sdelphij			info_auth_hashlen);
960285612Sdelphij		return 1;
961285612Sdelphij	}
962285612Sdelphij	return sendpkt(&qpkt, reqsize + maclen);
96354359Sroberto}
96454359Sroberto
96554359Sroberto
96654359Sroberto/*
96754359Sroberto * doquery - send a request and process the response
96854359Sroberto */
96954359Srobertoint
97054359Srobertodoquery(
97154359Sroberto	int implcode,
97254359Sroberto	int reqcode,
97354359Sroberto	int auth,
974293650Sglebius	size_t qitems,
975293650Sglebius	size_t qsize,
976293650Sglebius	const char *qdata,
977293650Sglebius	size_t *ritems,
978293650Sglebius	size_t *rsize,
979293650Sglebius	const char **rdata,
980132451Sroberto 	int quiet_mask,
981132451Sroberto	int esize
98254359Sroberto	)
98354359Sroberto{
98454359Sroberto	int res;
98554359Sroberto	char junk[512];
98654359Sroberto	fd_set fds;
987285612Sdelphij	struct sock_timeval tvzero;
98854359Sroberto
98954359Sroberto	/*
99054359Sroberto	 * Check to make sure host is open
99154359Sroberto	 */
99254359Sroberto	if (!havehost) {
99354359Sroberto		(void) fprintf(stderr, "***No host open, use `host' command\n");
99454359Sroberto		return -1;
99554359Sroberto	}
99654359Sroberto
99754359Sroberto	/*
99854359Sroberto	 * Poll the socket and clear out any pending data
99954359Sroberto	 */
1000106163Srobertoagain:
100154359Sroberto	do {
100254359Sroberto		tvzero.tv_sec = tvzero.tv_usec = 0;
100354359Sroberto		FD_ZERO(&fds);
100454359Sroberto		FD_SET(sockfd, &fds);
1005293650Sglebius		res = select(sockfd+1, &fds, NULL, NULL, &tvzero);
100654359Sroberto		if (res == -1) {
1007285612Sdelphij			warning("polling select");
100854359Sroberto			return -1;
100954359Sroberto		} else if (res > 0)
101054359Sroberto
101154359Sroberto		    (void) recv(sockfd, junk, sizeof junk, 0);
101254359Sroberto	} while (res > 0);
101354359Sroberto
101454359Sroberto
101554359Sroberto	/*
101654359Sroberto	 * send a request
101754359Sroberto	 */
101854359Sroberto	res = sendrequest(implcode, reqcode, auth, qitems, qsize, qdata);
101954359Sroberto	if (res != 0)
1020285612Sdelphij		return res;
102154359Sroberto
102254359Sroberto	/*
102354359Sroberto	 * Get the response.  If we got a standard error, print a message
102454359Sroberto	 */
1025132451Sroberto	res = getresponse(implcode, reqcode, ritems, rsize, rdata, esize);
102654359Sroberto
1027106163Sroberto	/*
1028106163Sroberto	 * Try to be compatible with older implementations of ntpd.
1029106163Sroberto	 */
1030106163Sroberto	if (res == INFO_ERR_FMT && req_pkt_size != 48) {
1031106163Sroberto		int oldsize;
1032106163Sroberto
1033106163Sroberto		oldsize = req_pkt_size;
1034106163Sroberto
1035106163Sroberto		switch(req_pkt_size) {
1036106163Sroberto		case REQ_LEN_NOMAC:
1037132451Sroberto			req_pkt_size = 160;
1038132451Sroberto			break;
1039132451Sroberto		case 160:
1040106163Sroberto			req_pkt_size = 48;
1041106163Sroberto			break;
1042106163Sroberto		}
1043132451Sroberto		if (impl_ver == IMPL_XNTPD) {
1044132451Sroberto			fprintf(stderr,
1045132451Sroberto			    "***Warning changing to older implementation\n");
1046132451Sroberto			return INFO_ERR_IMPL;
1047132451Sroberto		}
1048106163Sroberto
1049106163Sroberto		fprintf(stderr,
1050106163Sroberto		    "***Warning changing the request packet size from %d to %d\n",
1051106163Sroberto		    oldsize, req_pkt_size);
1052106163Sroberto		goto again;
1053106163Sroberto	}
1054106163Sroberto
105554359Sroberto 	/* log error message if not told to be quiet */
105654359Sroberto 	if ((res > 0) && (((1 << res) & quiet_mask) == 0)) {
105754359Sroberto		switch(res) {
1058285612Sdelphij		case INFO_ERR_IMPL:
1059132451Sroberto			/* Give us a chance to try the older implementation. */
1060132451Sroberto			if (implcode == IMPL_XNTPD)
1061132451Sroberto				break;
106254359Sroberto			(void) fprintf(stderr,
1063285612Sdelphij				       "***Server implementation incompatible with our own\n");
106454359Sroberto			break;
1065285612Sdelphij		case INFO_ERR_REQ:
106654359Sroberto			(void) fprintf(stderr,
106754359Sroberto				       "***Server doesn't implement this request\n");
106854359Sroberto			break;
1069285612Sdelphij		case INFO_ERR_FMT:
107054359Sroberto			(void) fprintf(stderr,
107154359Sroberto				       "***Server reports a format error in the received packet (shouldn't happen)\n");
107254359Sroberto			break;
1073285612Sdelphij		case INFO_ERR_NODATA:
107454359Sroberto			(void) fprintf(stderr,
107554359Sroberto				       "***Server reports data not found\n");
107654359Sroberto			break;
1077285612Sdelphij		case INFO_ERR_AUTH:
107854359Sroberto			(void) fprintf(stderr, "***Permission denied\n");
107954359Sroberto			break;
1080285612Sdelphij		case ERR_TIMEOUT:
108154359Sroberto			(void) fprintf(stderr, "***Request timed out\n");
108254359Sroberto			break;
1083285612Sdelphij		case ERR_INCOMPLETE:
108454359Sroberto			(void) fprintf(stderr,
108554359Sroberto				       "***Response from server was incomplete\n");
108654359Sroberto			break;
1087285612Sdelphij		default:
108854359Sroberto			(void) fprintf(stderr,
108954359Sroberto				       "***Server returns unknown error code %d\n", res);
109054359Sroberto			break;
109154359Sroberto		}
109254359Sroberto	}
109354359Sroberto	return res;
109454359Sroberto}
109554359Sroberto
109654359Sroberto
109754359Sroberto/*
109854359Sroberto * getcmds - read commands from the standard input and execute them
109954359Sroberto */
110054359Srobertostatic void
110154359Srobertogetcmds(void)
110254359Sroberto{
1103285612Sdelphij	char *	line;
1104285612Sdelphij	int	count;
110582498Sroberto
1106285612Sdelphij	ntp_readline_init(interactive ? prompt : NULL);
1107285612Sdelphij
110882498Sroberto	for (;;) {
1109285612Sdelphij		line = ntp_readline(&count);
1110285612Sdelphij		if (NULL == line)
1111285612Sdelphij			break;
111282498Sroberto		docmd(line);
111382498Sroberto		free(line);
111482498Sroberto	}
111554359Sroberto
1116285612Sdelphij	ntp_readline_uninit();
111754359Sroberto}
111854359Sroberto
111954359Sroberto
1120132451Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
112154359Sroberto/*
112254359Sroberto * abortcmd - catch interrupts and abort the current command
112354359Sroberto */
112454359Srobertostatic RETSIGTYPE
112554359Srobertoabortcmd(
112654359Sroberto	int sig
112754359Sroberto	)
112854359Sroberto{
112954359Sroberto	if (current_output == stdout)
1130338531Sdelphij		(void)fflush(stdout);
113154359Sroberto	putc('\n', stderr);
1132338531Sdelphij	(void)fflush(stderr);
1133338531Sdelphij	if (jump) {
1134338531Sdelphij		jump = 0;
1135338531Sdelphij		LONGJMP(interrupt_buf, 1);
1136338531Sdelphij	}
113754359Sroberto}
1138132451Sroberto#endif /* SYS_WINNT */
113954359Sroberto
114054359Sroberto/*
114154359Sroberto * docmd - decode the command line and execute a command
114254359Sroberto */
114354359Srobertostatic void
114454359Srobertodocmd(
114554359Sroberto	const char *cmdline
114654359Sroberto	)
114754359Sroberto{
1148182007Sroberto	char *tokens[1+MAXARGS+MOREARGS+2];
114954359Sroberto	struct parse pcmd;
115054359Sroberto	int ntok;
1151132451Sroberto	int i, ti;
1152132451Sroberto	int rval;
115354359Sroberto	struct xcmd *xcmd;
115454359Sroberto
1155132451Sroberto	ai_fam_templ = ai_fam_default;
115654359Sroberto	/*
115754359Sroberto	 * Tokenize the command line.  If nothing on it, return.
115854359Sroberto	 */
1159285612Sdelphij	if (strlen(cmdline) >= MAXLINE) {
1160285612Sdelphij		fprintf(stderr, "***Command ignored, more than %d characters:\n%s\n",
1161285612Sdelphij			MAXLINE - 1, cmdline);
1162285612Sdelphij		return;
1163285612Sdelphij	}
116454359Sroberto	tokenize(cmdline, tokens, &ntok);
116554359Sroberto	if (ntok == 0)
116654359Sroberto	    return;
116754359Sroberto
116854359Sroberto	/*
116954359Sroberto	 * Find the appropriate command description.
117054359Sroberto	 */
117154359Sroberto	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
117254359Sroberto	if (i == 0) {
117354359Sroberto		(void) fprintf(stderr, "***Command `%s' unknown\n",
117454359Sroberto			       tokens[0]);
117554359Sroberto		return;
117654359Sroberto	} else if (i >= 2) {
117754359Sroberto		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
117854359Sroberto			       tokens[0]);
117954359Sroberto		return;
118054359Sroberto	}
118154359Sroberto
118254359Sroberto	/*
118354359Sroberto	 * Save the keyword, then walk through the arguments, interpreting
118454359Sroberto	 * as we go.
118554359Sroberto	 */
118654359Sroberto	pcmd.keyword = tokens[0];
118754359Sroberto	pcmd.nargs = 0;
1188132451Sroberto	ti = 1;
1189132451Sroberto	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO;) {
1190132451Sroberto		if ((i+ti) >= ntok) {
119154359Sroberto			if (!(xcmd->arg[i] & OPT)) {
119254359Sroberto				printusage(xcmd, stderr);
119354359Sroberto				return;
119454359Sroberto			}
119554359Sroberto			break;
119654359Sroberto		}
1197132451Sroberto		if ((xcmd->arg[i] & OPT) && (*tokens[i+ti] == '>'))
1198132451Sroberto			break;
1199132451Sroberto		rval = getarg(tokens[i+ti], (int)xcmd->arg[i], &pcmd.argval[i]);
1200132451Sroberto		if (rval == -1) {
1201132451Sroberto			ti++;
1202132451Sroberto			continue;
1203132451Sroberto		}
1204132451Sroberto		if (rval == 0)
1205132451Sroberto			return;
120654359Sroberto		pcmd.nargs++;
1207132451Sroberto		i++;
120854359Sroberto	}
120954359Sroberto
1210182007Sroberto	/* Any extra args are assumed to be "OPT|NTP_STR". */
1211182007Sroberto	for ( ; i < MAXARGS + MOREARGS;) {
1212182007Sroberto	     if ((i+ti) >= ntok)
1213182007Sroberto		  break;
1214182007Sroberto		rval = getarg(tokens[i+ti], (int)(OPT|NTP_STR), &pcmd.argval[i]);
1215182007Sroberto		if (rval == -1) {
1216182007Sroberto			ti++;
1217182007Sroberto			continue;
1218182007Sroberto		}
1219182007Sroberto		if (rval == 0)
1220182007Sroberto			return;
1221182007Sroberto		pcmd.nargs++;
1222182007Sroberto		i++;
1223182007Sroberto	}
1224182007Sroberto
1225132451Sroberto	i += ti;
122654359Sroberto	if (i < ntok && *tokens[i] == '>') {
122754359Sroberto		char *fname;
122854359Sroberto
122954359Sroberto		if (*(tokens[i]+1) != '\0')
123054359Sroberto		    fname = tokens[i]+1;
123154359Sroberto		else if ((i+1) < ntok)
123254359Sroberto		    fname = tokens[i+1];
123354359Sroberto		else {
123454359Sroberto			(void) fprintf(stderr, "***No file for redirect\n");
123554359Sroberto			return;
123654359Sroberto		}
123754359Sroberto
123854359Sroberto		current_output = fopen(fname, "w");
123954359Sroberto		if (current_output == NULL) {
124054359Sroberto			(void) fprintf(stderr, "***Error opening %s: ", fname);
124154359Sroberto			perror("");
124254359Sroberto			return;
124354359Sroberto		}
124454359Sroberto	} else {
124554359Sroberto		current_output = stdout;
124654359Sroberto	}
124754359Sroberto
1248338531Sdelphij	if (interactive) {
1249338531Sdelphij		if ( ! SETJMP(interrupt_buf)) {
1250338531Sdelphij			jump = 1;
1251338531Sdelphij			(xcmd->handler)(&pcmd, current_output);
1252338531Sdelphij			jump = 0;
1253338531Sdelphij		} else {
1254338531Sdelphij			fflush(current_output);
1255338531Sdelphij			fputs("\n >>> command aborted <<<\n", stderr);
1256338531Sdelphij			fflush(stderr);
1257338531Sdelphij		}
125854359Sroberto	} else {
1259338531Sdelphij		jump = 0;
126054359Sroberto		(xcmd->handler)(&pcmd, current_output);
1261338531Sdelphij	}
1262338531Sdelphij	if ((NULL != current_output) && (stdout != current_output)) {
1263338531Sdelphij		(void)fclose(current_output);
1264132451Sroberto		current_output = NULL;
126554359Sroberto	}
126654359Sroberto}
126754359Sroberto
126854359Sroberto
126954359Sroberto/*
127054359Sroberto * tokenize - turn a command line into tokens
127154359Sroberto */
127254359Srobertostatic void
127354359Srobertotokenize(
127454359Sroberto	const char *line,
127554359Sroberto	char **tokens,
127654359Sroberto	int *ntok
127754359Sroberto	)
127854359Sroberto{
127954359Sroberto	register const char *cp;
128054359Sroberto	register char *sp;
128154359Sroberto	static char tspace[MAXLINE];
128254359Sroberto
128354359Sroberto	sp = tspace;
128454359Sroberto	cp = line;
128554359Sroberto	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
128654359Sroberto		tokens[*ntok] = sp;
128754359Sroberto		while (ISSPACE(*cp))
128854359Sroberto		    cp++;
128954359Sroberto		if (ISEOL(*cp))
129054359Sroberto		    break;
129154359Sroberto		do {
129254359Sroberto			*sp++ = *cp++;
129354359Sroberto		} while (!ISSPACE(*cp) && !ISEOL(*cp));
129454359Sroberto
129554359Sroberto		*sp++ = '\0';
129654359Sroberto	}
129754359Sroberto}
129854359Sroberto
129954359Sroberto
130054359Sroberto
130154359Sroberto/*
130254359Sroberto * findcmd - find a command in a command description table
130354359Sroberto */
130454359Srobertostatic int
130554359Srobertofindcmd(
130654359Sroberto	register char *str,
130754359Sroberto	struct xcmd *clist1,
130854359Sroberto	struct xcmd *clist2,
130954359Sroberto	struct xcmd **cmd
131054359Sroberto	)
131154359Sroberto{
131254359Sroberto	register struct xcmd *cl;
1313293650Sglebius	size_t clen;
131454359Sroberto	int nmatch;
131554359Sroberto	struct xcmd *nearmatch = NULL;
131654359Sroberto	struct xcmd *clist;
131754359Sroberto
131854359Sroberto	clen = strlen(str);
131954359Sroberto	nmatch = 0;
132054359Sroberto	if (clist1 != 0)
132154359Sroberto	    clist = clist1;
132254359Sroberto	else if (clist2 != 0)
132354359Sroberto	    clist = clist2;
132454359Sroberto	else
132554359Sroberto	    return 0;
132654359Sroberto
132754359Sroberto    again:
132854359Sroberto	for (cl = clist; cl->keyword != 0; cl++) {
132954359Sroberto		/* do a first character check, for efficiency */
133054359Sroberto		if (*str != *(cl->keyword))
133154359Sroberto		    continue;
133254359Sroberto		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
133354359Sroberto			/*
133454359Sroberto			 * Could be extact match, could be approximate.
133554359Sroberto			 * Is exact if the length of the keyword is the
133654359Sroberto			 * same as the str.
133754359Sroberto			 */
133854359Sroberto			if (*((cl->keyword) + clen) == '\0') {
133954359Sroberto				*cmd = cl;
134054359Sroberto				return 1;
134154359Sroberto			}
134254359Sroberto			nmatch++;
134354359Sroberto			nearmatch = cl;
134454359Sroberto		}
134554359Sroberto	}
134654359Sroberto
134754359Sroberto				/*
134854359Sroberto				 * See if there is more to do.  If so, go again.  Sorry about the
134954359Sroberto				 * goto, too much looking at BSD sources...
135054359Sroberto				 */
135154359Sroberto	if (clist == clist1 && clist2 != 0) {
135254359Sroberto		clist = clist2;
135354359Sroberto		goto again;
135454359Sroberto	}
135554359Sroberto
135654359Sroberto				/*
135754359Sroberto				 * If we got extactly 1 near match, use it, else return number
135854359Sroberto				 * of matches.
135954359Sroberto				 */
136054359Sroberto	if (nmatch == 1) {
136154359Sroberto		*cmd = nearmatch;
136254359Sroberto		return 1;
136354359Sroberto	}
136454359Sroberto	return nmatch;
136554359Sroberto}
136654359Sroberto
136754359Sroberto
1368132451Sroberto/*
136954359Sroberto * getarg - interpret an argument token
1370132451Sroberto *
1371182007Sroberto * string is always set.
1372182007Sroberto * type is set to the decoded type.
1373182007Sroberto *
1374132451Sroberto * return:	 0 - failure
1375132451Sroberto *		 1 - success
1376132451Sroberto *		-1 - skip to next token
137754359Sroberto */
137854359Srobertostatic int
137954359Srobertogetarg(
138054359Sroberto	char *str,
138154359Sroberto	int code,
138254359Sroberto	arg_v *argp
138354359Sroberto	)
138454359Sroberto{
138554359Sroberto	int isneg;
138654359Sroberto	char *cp, *np;
138754359Sroberto	static const char *digits = "0123456789";
138854359Sroberto
1389285612Sdelphij	ZERO(*argp);
1390182007Sroberto	argp->string = str;
1391182007Sroberto	argp->type   = code & ~OPT;
1392182007Sroberto
1393182007Sroberto	switch (argp->type) {
139454359Sroberto	    case NTP_STR:
139554359Sroberto		break;
1396182007Sroberto	    case NTP_ADD:
1397132451Sroberto		if (!strcmp("-6", str)) {
1398132451Sroberto			ai_fam_templ = AF_INET6;
1399132451Sroberto			return -1;
1400132451Sroberto		} else if (!strcmp("-4", str)) {
1401132451Sroberto			ai_fam_templ = AF_INET;
1402132451Sroberto			return -1;
1403132451Sroberto		}
1404132451Sroberto		if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) {
140554359Sroberto			return 0;
140654359Sroberto		}
140754359Sroberto		break;
1408182007Sroberto	    case NTP_INT:
1409182007Sroberto	    case NTP_UINT:
141054359Sroberto		isneg = 0;
141154359Sroberto		np = str;
141254359Sroberto		if (*np == '-') {
141354359Sroberto			np++;
141454359Sroberto			isneg = 1;
141554359Sroberto		}
141654359Sroberto
141754359Sroberto		argp->uval = 0;
141854359Sroberto		do {
141954359Sroberto			cp = strchr(digits, *np);
142054359Sroberto			if (cp == NULL) {
142154359Sroberto				(void) fprintf(stderr,
142254359Sroberto					       "***Illegal integer value %s\n", str);
142354359Sroberto				return 0;
142454359Sroberto			}
142554359Sroberto			argp->uval *= 10;
1426293650Sglebius			argp->uval += (u_long)(cp - digits);
142754359Sroberto		} while (*(++np) != '\0');
142854359Sroberto
142954359Sroberto		if (isneg) {
1430182007Sroberto			if ((code & ~OPT) == NTP_UINT) {
143154359Sroberto				(void) fprintf(stderr,
143254359Sroberto					       "***Value %s should be unsigned\n", str);
143354359Sroberto				return 0;
143454359Sroberto			}
143554359Sroberto			argp->ival = -argp->ival;
143654359Sroberto		}
143754359Sroberto		break;
1438132451Sroberto	    case IP_VERSION:
1439132451Sroberto		if (!strcmp("-6", str))
1440132451Sroberto			argp->ival = 6 ;
1441132451Sroberto		else if (!strcmp("-4", str))
1442132451Sroberto			argp->ival = 4 ;
1443132451Sroberto		else {
1444132451Sroberto			(void) fprintf(stderr,
1445132451Sroberto			    "***Version must be either 4 or 6\n");
1446132451Sroberto			return 0;
1447132451Sroberto		}
1448132451Sroberto		break;
144954359Sroberto	}
145054359Sroberto
145154359Sroberto	return 1;
145254359Sroberto}
145354359Sroberto
145454359Sroberto
145554359Sroberto/*
145654359Sroberto * getnetnum - given a host name, return its net number
145754359Sroberto *	       and (optional) full name
145854359Sroberto */
145954359Srobertostatic int
146054359Srobertogetnetnum(
146154359Sroberto	const char *hname,
1462285612Sdelphij	sockaddr_u *num,
1463132451Sroberto	char *fullhost,
1464132451Sroberto	int af
146554359Sroberto	)
146654359Sroberto{
1467132451Sroberto	struct addrinfo hints, *ai = NULL;
146854359Sroberto
1469285612Sdelphij	ZERO(hints);
1470132451Sroberto	hints.ai_flags = AI_CANONNAME;
1471132451Sroberto#ifdef AI_ADDRCONFIG
1472132451Sroberto	hints.ai_flags |= AI_ADDRCONFIG;
1473132451Sroberto#endif
1474132451Sroberto
1475285612Sdelphij	/*
1476285612Sdelphij	 * decodenetnum only works with addresses, but handles syntax
1477285612Sdelphij	 * that getaddrinfo doesn't:  [2001::1]:1234
1478285612Sdelphij	 */
147954359Sroberto	if (decodenetnum(hname, num)) {
1480285612Sdelphij		if (fullhost != NULL)
1481285612Sdelphij			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1482285612Sdelphij				    LENHOSTNAME, NULL, 0, 0);
148354359Sroberto		return 1;
1484182007Sroberto	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1485289997Sglebius		INSIST(sizeof(*num) >= ai->ai_addrlen);
1486285612Sdelphij		memcpy(num, ai->ai_addr, ai->ai_addrlen);
1487285612Sdelphij		if (fullhost != NULL) {
1488285612Sdelphij			if (ai->ai_canonname != NULL)
1489285612Sdelphij				strlcpy(fullhost, ai->ai_canonname,
1490285612Sdelphij					LENHOSTNAME);
1491285612Sdelphij			else
1492285612Sdelphij				getnameinfo(&num->sa, SOCKLEN(num),
1493285612Sdelphij					    fullhost, LENHOSTNAME, NULL,
1494285612Sdelphij					    0, 0);
1495285612Sdelphij		}
149654359Sroberto		return 1;
149754359Sroberto	}
1498285612Sdelphij	fprintf(stderr, "***Can't find host %s\n", hname);
1499285612Sdelphij
1500285612Sdelphij	return 0;
150154359Sroberto}
150254359Sroberto
1503285612Sdelphij
150454359Sroberto/*
150554359Sroberto * nntohost - convert network number to host name.  This routine enforces
150654359Sroberto *	       the showhostnames setting.
150754359Sroberto */
1508285612Sdelphijconst char *
150954359Srobertonntohost(
1510285612Sdelphij	sockaddr_u *netnum
151154359Sroberto	)
151254359Sroberto{
1513285612Sdelphij	if (!showhostnames || SOCK_UNSPEC(netnum))
1514285612Sdelphij		return stoa(netnum);
1515285612Sdelphij	else if (ISREFCLOCKADR(netnum))
1516132451Sroberto		return refnumtoa(netnum);
1517285612Sdelphij	else
1518285612Sdelphij		return socktohost(netnum);
151954359Sroberto}
152054359Sroberto
152154359Sroberto
152254359Sroberto/*
152354359Sroberto * Finally, the built in command handlers
152454359Sroberto */
152554359Sroberto
152654359Sroberto/*
152754359Sroberto * help - tell about commands, or details of a particular command
152854359Sroberto */
152954359Srobertostatic void
153054359Srobertohelp(
153154359Sroberto	struct parse *pcmd,
153254359Sroberto	FILE *fp
153354359Sroberto	)
153454359Sroberto{
153554359Sroberto	struct xcmd *xcp;
153654359Sroberto	char *cmd;
1537182007Sroberto	const char *list[100];
1538285612Sdelphij	size_t word, words;
1539285612Sdelphij	size_t row, rows;
1540285612Sdelphij	size_t col, cols;
1541285612Sdelphij	size_t length;
154254359Sroberto
154354359Sroberto	if (pcmd->nargs == 0) {
1544182007Sroberto		words = 0;
154554359Sroberto		for (xcp = builtins; xcp->keyword != 0; xcp++) {
154654359Sroberto			if (*(xcp->keyword) != '?')
1547285612Sdelphij				list[words++] = xcp->keyword;
154854359Sroberto		}
1549285612Sdelphij		for (xcp = opcmds; xcp->keyword != 0; xcp++)
1550285612Sdelphij			list[words++] = xcp->keyword;
155154359Sroberto
1552285612Sdelphij		qsort((void *)list, words, sizeof(list[0]), helpsort);
1553182007Sroberto		col = 0;
1554182007Sroberto		for (word = 0; word < words; word++) {
1555285612Sdelphij			length = strlen(list[word]);
1556285612Sdelphij			col = max(col, length);
155754359Sroberto		}
155854359Sroberto
1559182007Sroberto		cols = SCREENWIDTH / ++col;
1560285612Sdelphij		rows = (words + cols - 1) / cols;
1561182007Sroberto
1562285612Sdelphij		fprintf(fp, "ntpdc commands:\n");
1563182007Sroberto
1564182007Sroberto		for (row = 0; row < rows; row++) {
1565285612Sdelphij			for (word = row; word < words; word += rows)
1566285612Sdelphij				fprintf(fp, "%-*.*s", (int)col,
1567285612Sdelphij					(int)col - 1, list[word]);
1568285612Sdelphij			fprintf(fp, "\n");
156954359Sroberto		}
157054359Sroberto	} else {
157154359Sroberto		cmd = pcmd->argval[0].string;
1572182007Sroberto		words = findcmd(cmd, builtins, opcmds, &xcp);
1573182007Sroberto		if (words == 0) {
1574285612Sdelphij			fprintf(stderr,
1575285612Sdelphij				"Command `%s' is unknown\n", cmd);
157654359Sroberto			return;
1577182007Sroberto		} else if (words >= 2) {
1578285612Sdelphij			fprintf(stderr,
1579285612Sdelphij				"Command `%s' is ambiguous\n", cmd);
158054359Sroberto			return;
158154359Sroberto		}
1582285612Sdelphij		fprintf(fp, "function: %s\n", xcp->comment);
158354359Sroberto		printusage(xcp, fp);
158454359Sroberto	}
158554359Sroberto}
158654359Sroberto
158754359Sroberto
158854359Sroberto/*
158954359Sroberto * helpsort - do hostname qsort comparisons
159054359Sroberto */
159154359Srobertostatic int
159254359Srobertohelpsort(
159354359Sroberto	const void *t1,
159454359Sroberto	const void *t2
159554359Sroberto	)
159654359Sroberto{
1597285612Sdelphij	const char * const *	name1 = t1;
1598285612Sdelphij	const char * const *	name2 = t2;
159954359Sroberto
160054359Sroberto	return strcmp(*name1, *name2);
160154359Sroberto}
160254359Sroberto
160354359Sroberto
160454359Sroberto/*
160554359Sroberto * printusage - print usage information for a command
160654359Sroberto */
160754359Srobertostatic void
160854359Srobertoprintusage(
160954359Sroberto	struct xcmd *xcp,
161054359Sroberto	FILE *fp
161154359Sroberto	)
161254359Sroberto{
1613132451Sroberto	int i, opt46;
161454359Sroberto
1615132451Sroberto	opt46 = 0;
161654359Sroberto	(void) fprintf(fp, "usage: %s", xcp->keyword);
161754359Sroberto	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
1618182007Sroberto		if (opt46 == 0 && (xcp->arg[i] & ~OPT) == NTP_ADD) {
1619132451Sroberto			(void) fprintf(fp, " [ -4|-6 ]");
1620132451Sroberto			opt46 = 1;
1621132451Sroberto		}
162254359Sroberto		if (xcp->arg[i] & OPT)
162354359Sroberto		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
162454359Sroberto		else
162554359Sroberto		    (void) fprintf(fp, " %s", xcp->desc[i]);
162654359Sroberto	}
162754359Sroberto	(void) fprintf(fp, "\n");
162854359Sroberto}
162954359Sroberto
163054359Sroberto
163154359Sroberto/*
163254359Sroberto * timeout - set time out time
163354359Sroberto */
163454359Srobertostatic void
163554359Srobertotimeout(
163654359Sroberto	struct parse *pcmd,
163754359Sroberto	FILE *fp
163854359Sroberto	)
163954359Sroberto{
164054359Sroberto	int val;
164154359Sroberto
164254359Sroberto	if (pcmd->nargs == 0) {
164354359Sroberto		val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
164454359Sroberto		(void) fprintf(fp, "primary timeout %d ms\n", val);
164554359Sroberto	} else {
164654359Sroberto		tvout.tv_sec = pcmd->argval[0].uval / 1000;
164754359Sroberto		tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
164854359Sroberto			* 1000;
164954359Sroberto	}
165054359Sroberto}
165154359Sroberto
165254359Sroberto
165354359Sroberto/*
165454359Sroberto * my_delay - set delay for auth requests
165554359Sroberto */
165654359Srobertostatic void
165754359Srobertomy_delay(
165854359Sroberto	struct parse *pcmd,
165954359Sroberto	FILE *fp
166054359Sroberto	)
166154359Sroberto{
166254359Sroberto	int isneg;
166354359Sroberto	u_long val;
166454359Sroberto
166554359Sroberto	if (pcmd->nargs == 0) {
166654359Sroberto		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
166754359Sroberto		(void) fprintf(fp, "delay %lu ms\n", val);
166854359Sroberto	} else {
166954359Sroberto		if (pcmd->argval[0].ival < 0) {
167054359Sroberto			isneg = 1;
167154359Sroberto			val = (u_long)(-pcmd->argval[0].ival);
167254359Sroberto		} else {
167354359Sroberto			isneg = 0;
167454359Sroberto			val = (u_long)pcmd->argval[0].ival;
167554359Sroberto		}
167654359Sroberto
167754359Sroberto		delay_time.l_ui = val / 1000;
167854359Sroberto		val %= 1000;
167954359Sroberto		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
168054359Sroberto
168154359Sroberto		if (isneg)
168254359Sroberto		    L_NEG(&delay_time);
168354359Sroberto	}
168454359Sroberto}
168554359Sroberto
168654359Sroberto
168754359Sroberto/*
168854359Sroberto * host - set the host we are dealing with.
168954359Sroberto */
169054359Srobertostatic void
169154359Srobertohost(
169254359Sroberto	struct parse *pcmd,
169354359Sroberto	FILE *fp
169454359Sroberto	)
169554359Sroberto{
1696132451Sroberto	int i;
1697132451Sroberto
169854359Sroberto	if (pcmd->nargs == 0) {
169954359Sroberto		if (havehost)
170054359Sroberto		    (void) fprintf(fp, "current host is %s\n", currenthost);
170154359Sroberto		else
170254359Sroberto		    (void) fprintf(fp, "no current host\n");
1703132451Sroberto		return;
1704132451Sroberto	}
1705132451Sroberto
1706132451Sroberto	i = 0;
1707132451Sroberto	if (pcmd->nargs == 2) {
1708132451Sroberto		if (!strcmp("-4", pcmd->argval[i].string))
1709132451Sroberto			ai_fam_templ = AF_INET;
1710132451Sroberto		else if (!strcmp("-6", pcmd->argval[i].string))
1711132451Sroberto			ai_fam_templ = AF_INET6;
1712132451Sroberto		else {
1713132451Sroberto			if (havehost)
1714132451Sroberto				(void) fprintf(fp,
1715132451Sroberto				    "current host remains %s\n", currenthost);
1716132451Sroberto			else
1717132451Sroberto				(void) fprintf(fp, "still no current host\n");
1718132451Sroberto			return;
1719132451Sroberto		}
1720132451Sroberto		i = 1;
1721132451Sroberto	}
1722132451Sroberto	if (openhost(pcmd->argval[i].string)) {
172354359Sroberto		(void) fprintf(fp, "current host set to %s\n", currenthost);
172454359Sroberto	} else {
172554359Sroberto		if (havehost)
172654359Sroberto		    (void) fprintf(fp,
172754359Sroberto				   "current host remains %s\n", currenthost);
172854359Sroberto		else
172954359Sroberto		    (void) fprintf(fp, "still no current host\n");
173054359Sroberto	}
173154359Sroberto}
173254359Sroberto
173354359Sroberto
173454359Sroberto/*
173554359Sroberto * keyid - get a keyid to use for authenticating requests
173654359Sroberto */
173754359Srobertostatic void
173854359Srobertokeyid(
173954359Sroberto	struct parse *pcmd,
174054359Sroberto	FILE *fp
174154359Sroberto	)
174254359Sroberto{
174354359Sroberto	if (pcmd->nargs == 0) {
1744182007Sroberto		if (info_auth_keyid == 0 && !keyid_entered)
174554359Sroberto		    (void) fprintf(fp, "no keyid defined\n");
1746182007Sroberto		else if (info_auth_keyid == 0 && keyid_entered)
1747182007Sroberto		    (void) fprintf(fp, "no keyid will be sent\n");
174854359Sroberto		else
174954359Sroberto		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
175054359Sroberto	} else {
175154359Sroberto		info_auth_keyid = pcmd->argval[0].uval;
1752182007Sroberto		keyid_entered = 1;
175354359Sroberto	}
175454359Sroberto}
175554359Sroberto
175654359Sroberto
175754359Sroberto/*
175854359Sroberto * keytype - get type of key to use for authenticating requests
175954359Sroberto */
176054359Srobertostatic void
176154359Srobertokeytype(
176254359Sroberto	struct parse *pcmd,
176354359Sroberto	FILE *fp
176454359Sroberto	)
176554359Sroberto{
1766285612Sdelphij	const char *	digest_name;
1767285612Sdelphij	size_t		digest_len;
1768285612Sdelphij	int		key_type;
176954359Sroberto
1770285612Sdelphij	if (!pcmd->nargs) {
1771285612Sdelphij		fprintf(fp, "keytype is %s with %lu octet digests\n",
1772285612Sdelphij			keytype_name(info_auth_keytype),
1773285612Sdelphij			(u_long)info_auth_hashlen);
1774285612Sdelphij		return;
1775285612Sdelphij	}
1776285612Sdelphij
1777285612Sdelphij	digest_name = pcmd->argval[0].string;
1778285612Sdelphij	digest_len = 0;
1779285612Sdelphij	key_type = keytype_from_text(digest_name, &digest_len);
1780285612Sdelphij
1781285612Sdelphij	if (!key_type) {
1782285612Sdelphij		fprintf(fp, "keytype must be 'md5'%s\n",
1783285612Sdelphij#ifdef OPENSSL
1784285612Sdelphij			" or a digest type provided by OpenSSL");
1785285612Sdelphij#else
1786285612Sdelphij			"");
1787285612Sdelphij#endif
1788285612Sdelphij		return;
1789285612Sdelphij	}
1790285612Sdelphij
1791285612Sdelphij	info_auth_keytype = key_type;
1792285612Sdelphij	info_auth_hashlen = digest_len;
179354359Sroberto}
179454359Sroberto
179554359Sroberto
179654359Sroberto/*
179754359Sroberto * passwd - get an authentication key
179854359Sroberto */
179954359Sroberto/*ARGSUSED*/
180054359Srobertostatic void
180154359Srobertopasswd(
180254359Sroberto	struct parse *pcmd,
180354359Sroberto	FILE *fp
180454359Sroberto	)
180554359Sroberto{
180654359Sroberto	char *pass;
180754359Sroberto
180854359Sroberto	if (info_auth_keyid == 0) {
180954359Sroberto		info_auth_keyid = getkeyid("Keyid: ");
181054359Sroberto		if (info_auth_keyid == 0) {
181154359Sroberto			(void)fprintf(fp, "Keyid must be defined\n");
181254359Sroberto			return;
181354359Sroberto		}
181454359Sroberto	}
1815285612Sdelphij	if (pcmd->nargs >= 1)
1816285612Sdelphij		pass = pcmd->argval[0].string;
1817285612Sdelphij	else {
1818285612Sdelphij		pass = getpass_keytype(info_auth_keytype);
1819285612Sdelphij		if ('\0' == *pass) {
1820285612Sdelphij			fprintf(fp, "Password unchanged\n");
1821285612Sdelphij			return;
182282498Sroberto		}
182354359Sroberto	}
1824285612Sdelphij	authusekey(info_auth_keyid, info_auth_keytype, (u_char *)pass);
1825285612Sdelphij	authtrust(info_auth_keyid, 1);
182654359Sroberto}
182754359Sroberto
182854359Sroberto
182954359Sroberto/*
183054359Sroberto * hostnames - set the showhostnames flag
183154359Sroberto */
183254359Srobertostatic void
183354359Srobertohostnames(
183454359Sroberto	struct parse *pcmd,
183554359Sroberto	FILE *fp
183654359Sroberto	)
183754359Sroberto{
183854359Sroberto	if (pcmd->nargs == 0) {
183954359Sroberto		if (showhostnames)
184054359Sroberto		    (void) fprintf(fp, "hostnames being shown\n");
184154359Sroberto		else
184254359Sroberto		    (void) fprintf(fp, "hostnames not being shown\n");
184354359Sroberto	} else {
184454359Sroberto		if (STREQ(pcmd->argval[0].string, "yes"))
184554359Sroberto		    showhostnames = 1;
184654359Sroberto		else if (STREQ(pcmd->argval[0].string, "no"))
184754359Sroberto		    showhostnames = 0;
184854359Sroberto		else
184954359Sroberto		    (void)fprintf(stderr, "What?\n");
185054359Sroberto	}
185154359Sroberto}
185254359Sroberto
185354359Sroberto
185454359Sroberto/*
185554359Sroberto * setdebug - set/change debugging level
185654359Sroberto */
185754359Srobertostatic void
185854359Srobertosetdebug(
185954359Sroberto	struct parse *pcmd,
186054359Sroberto	FILE *fp
186154359Sroberto	)
186254359Sroberto{
186354359Sroberto	if (pcmd->nargs == 0) {
186454359Sroberto		(void) fprintf(fp, "debug level is %d\n", debug);
186554359Sroberto		return;
186654359Sroberto	} else if (STREQ(pcmd->argval[0].string, "no")) {
186754359Sroberto		debug = 0;
186854359Sroberto	} else if (STREQ(pcmd->argval[0].string, "more")) {
186954359Sroberto		debug++;
187054359Sroberto	} else if (STREQ(pcmd->argval[0].string, "less")) {
187154359Sroberto		debug--;
187254359Sroberto	} else {
187354359Sroberto		(void) fprintf(fp, "What?\n");
187454359Sroberto		return;
187554359Sroberto	}
187654359Sroberto	(void) fprintf(fp, "debug level set to %d\n", debug);
187754359Sroberto}
187854359Sroberto
187954359Sroberto
188054359Sroberto/*
188154359Sroberto * quit - stop this nonsense
188254359Sroberto */
188354359Sroberto/*ARGSUSED*/
188454359Srobertostatic void
188554359Srobertoquit(
188654359Sroberto	struct parse *pcmd,
188754359Sroberto	FILE *fp
188854359Sroberto	)
188954359Sroberto{
189054359Sroberto	if (havehost)
189154359Sroberto	    closesocket(sockfd);
189254359Sroberto	exit(0);
189354359Sroberto}
189454359Sroberto
189554359Sroberto
189654359Sroberto/*
189754359Sroberto * version - print the current version number
189854359Sroberto */
189954359Sroberto/*ARGSUSED*/
190054359Srobertostatic void
190154359Srobertoversion(
190254359Sroberto	struct parse *pcmd,
190354359Sroberto	FILE *fp
190454359Sroberto	)
190554359Sroberto{
190654359Sroberto
190754359Sroberto	(void) fprintf(fp, "%s\n", Version);
190854359Sroberto	return;
190954359Sroberto}
191054359Sroberto
191154359Sroberto
1912285612Sdelphijstatic void __attribute__((__format__(__printf__, 1, 0)))
1913285612Sdelphijvwarning(const char *fmt, va_list ap)
1914285612Sdelphij{
1915285612Sdelphij	int serrno = errno;
1916285612Sdelphij	(void) fprintf(stderr, "%s: ", progname);
1917285612Sdelphij	vfprintf(stderr, fmt, ap);
1918285612Sdelphij	(void) fprintf(stderr, ": %s\n", strerror(serrno));
1919285612Sdelphij}
1920285612Sdelphij
192154359Sroberto/*
192254359Sroberto * warning - print a warning message
192354359Sroberto */
1924285612Sdelphijstatic void __attribute__((__format__(__printf__, 1, 2)))
192554359Srobertowarning(
192654359Sroberto	const char *fmt,
1927285612Sdelphij	...
192854359Sroberto	)
192954359Sroberto{
1930285612Sdelphij	va_list ap;
1931285612Sdelphij	va_start(ap, fmt);
1932285612Sdelphij	vwarning(fmt, ap);
1933285612Sdelphij	va_end(ap);
193454359Sroberto}
193554359Sroberto
193654359Sroberto
193754359Sroberto/*
193854359Sroberto * error - print a message and exit
193954359Sroberto */
1940285612Sdelphijstatic void __attribute__((__format__(__printf__, 1, 2)))
194154359Srobertoerror(
194254359Sroberto	const char *fmt,
1943285612Sdelphij	...
194454359Sroberto	)
194554359Sroberto{
1946285612Sdelphij	va_list ap;
1947285612Sdelphij	va_start(ap, fmt);
1948285612Sdelphij	vwarning(fmt, ap);
1949285612Sdelphij	va_end(ap);
195054359Sroberto	exit(1);
195154359Sroberto}
195254359Sroberto
195354359Sroberto/*
195454359Sroberto * getkeyid - prompt the user for a keyid to use
195554359Sroberto */
195654359Srobertostatic u_long
195754359Srobertogetkeyid(
195854359Sroberto	const char *keyprompt
195954359Sroberto	)
196054359Sroberto{
1961285612Sdelphij	int c;
196254359Sroberto	FILE *fi;
196354359Sroberto	char pbuf[20];
1964285612Sdelphij	size_t i;
1965285612Sdelphij	size_t ilim;
196654359Sroberto
196754359Sroberto#ifndef SYS_WINNT
196854359Sroberto	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
196954359Sroberto#else
1970285612Sdelphij	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
197154359Sroberto#endif /* SYS_WINNT */
197254359Sroberto		fi = stdin;
1973285612Sdelphij	else
197454359Sroberto		setbuf(fi, (char *)NULL);
197554359Sroberto	fprintf(stderr, "%s", keyprompt); fflush(stderr);
1976285612Sdelphij	for (i = 0, ilim = COUNTOF(pbuf) - 1;
1977285612Sdelphij	     i < ilim && (c = getc(fi)) != '\n' && c != EOF;
1978285612Sdelphij	     )
1979285612Sdelphij		pbuf[i++] = (char)c;
1980285612Sdelphij	pbuf[i] = '\0';
198154359Sroberto	if (fi != stdin)
1982285612Sdelphij		fclose(fi);
1983285612Sdelphij
1984285612Sdelphij	return (u_long) atoi(pbuf);
198554359Sroberto}
1986