1/*
2 * ntpdc - control and monitor your ntpd daemon
3 */
4
5#include <stdio.h>
6
7#include <ctype.h>
8#include <signal.h>
9#include <setjmp.h>
10
11#include "ntpdc.h"
12#include "ntp_select.h"
13#include "ntp_io.h"
14#include "ntp_stdlib.h"
15/* Don't include ISC's version of IPv6 variables and structures */
16#define ISC_IPV6_H 1
17#include "isc/net.h"
18#include "isc/result.h"
19
20#include "ntpdc-opts.h"
21
22#ifdef SYS_WINNT
23# include <Mswsock.h>
24# include <io.h>
25#else
26# define closesocket close
27#endif /* SYS_WINNT */
28
29#if defined(HAVE_LIBREADLINE) || defined (HAVE_LIBEDIT)
30# include <readline/readline.h>
31# include <readline/history.h>
32#endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */
33
34#ifdef SYS_VXWORKS
35				/* vxWorks needs mode flag -casey*/
36# define open(name, flags)   open(name, flags, 0777)
37# define SERVER_PORT_NUM     123
38#endif
39
40/* We use COMMAND as an autogen keyword */
41#ifdef COMMAND
42# undef COMMAND
43#endif
44
45/*
46 * Because we now potentially understand a lot of commands (and
47 * it requires a lot of commands to talk to ntpd) we will run
48 * interactive if connected to a terminal.
49 */
50static	int	interactive = 0;	/* set to 1 when we should prompt */
51static	const char *	prompt = "ntpdc> ";	/* prompt to ask him about */
52
53/*
54 * Keyid used for authenticated requests.  Obtained on the fly.
55 */
56static	u_long	info_auth_keyid;
57static int keyid_entered = 0;
58
59/*
60 * Type of key md5
61 */
62#define	KEY_TYPE_MD5	4
63
64static	int info_auth_keytype = KEY_TYPE_MD5;	/* MD5 */
65u_long	current_time;		/* needed by authkeys; not used */
66
67/*
68 * for get_systime()
69 */
70s_char	sys_precision;		/* local clock precision (log2 s) */
71
72int		ntpdcmain	P((int,	char **));
73/*
74 * Built in command handler declarations
75 */
76static	int	openhost	P((const char *));
77static	int	sendpkt		P((char *, int));
78static	void	growpktdata	P((void));
79static	int	getresponse	P((int, int, int *, int *, char **, int));
80static	int	sendrequest	P((int, int, int, int, int, char *));
81static	void	getcmds		P((void));
82static	RETSIGTYPE abortcmd	P((int));
83static	void	docmd		P((const char *));
84static	void	tokenize	P((const char *, char **, int *));
85static	int	findcmd		P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
86static	int	getarg		P((char *, int, arg_v *));
87static	int	getnetnum	P((const char *, struct sockaddr_storage *, char *, int));
88static	void	help		P((struct parse *, FILE *));
89#ifdef QSORT_USES_VOID_P
90static	int	helpsort	P((const void *, const void *));
91#else
92static	int	helpsort	P((char **, char **));
93#endif
94static	void	printusage	P((struct xcmd *, FILE *));
95static	void	timeout		P((struct parse *, FILE *));
96static	void	my_delay	P((struct parse *, FILE *));
97static	void	host		P((struct parse *, FILE *));
98static	void	keyid		P((struct parse *, FILE *));
99static	void	keytype		P((struct parse *, FILE *));
100static	void	passwd		P((struct parse *, FILE *));
101static	void	hostnames	P((struct parse *, FILE *));
102static	void	setdebug	P((struct parse *, FILE *));
103static	void	quit		P((struct parse *, FILE *));
104static	void	version		P((struct parse *, FILE *));
105static	void	warning		P((const char *, const char *, const char *));
106static	void	error		P((const char *, const char *, const char *));
107static	u_long	getkeyid	P((const char *));
108
109
110
111/*
112 * Built-in commands we understand
113 */
114static	struct xcmd builtins[] = {
115	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
116	  { "command", "", "", "" },
117	  "tell the use and syntax of commands" },
118	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
119	  { "command", "", "", "" },
120	  "tell the use and syntax of commands" },
121	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
122	  { "msec", "", "", "" },
123	  "set the primary receive time out" },
124	{ "delay",	my_delay,	{ OPT|NTP_INT, NO, NO, NO },
125	  { "msec", "", "", "" },
126	  "set the delay added to encryption time stamps" },
127	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
128	  { "-4|-6", "hostname", "", "" },
129	  "specify the host whose NTP server we talk to" },
130	{ "passwd",	passwd,		{ OPT|NTP_STR, NO, NO, NO },
131	  { "", "", "", "" },
132	  "specify a password to use for authenticated requests"},
133	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
134	  { "yes|no", "", "", "" },
135	  "specify whether hostnames or net numbers are printed"},
136	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
137	  { "no|more|less", "", "", "" },
138	  "set/change debugging level" },
139	{ "quit",	quit,		{ NO, NO, NO, NO },
140	  { "", "", "", "" },
141	  "exit ntpdc" },
142	{ "exit",	quit,		{ NO, NO, NO, NO },
143	  { "", "", "", "" },
144	  "exit ntpdc" },
145	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
146	  { "key#", "", "", "" },
147	  "set/show keyid to use for authenticated requests" },
148	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
149	  { "(md5|des)", "", "", "" },
150	  "set/show key authentication type for authenticated requests (des|md5)" },
151	{ "version",	version,	{ NO, NO, NO, NO },
152	  { "", "", "", "" },
153	  "print version number" },
154	{ 0,		0,		{ NO, NO, NO, NO },
155	  { "", "", "", "" }, "" }
156};
157
158
159/*
160 * Default values we use.
161 */
162#define	DEFTIMEOUT	(5)		/* 5 second time out */
163#define	DEFSTIMEOUT	(2)		/* 2 second time out after first */
164#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
165#define	DEFHOST		"localhost"	/* default host name */
166#define	LENHOSTNAME	256		/* host name is 256 characters long */
167#define	MAXCMDS		100		/* maximum commands on cmd line */
168#define	MAXHOSTS	200		/* maximum hosts on cmd line */
169#define	MAXLINE		512		/* maximum line length */
170#define	MAXTOKENS	(1+1+MAXARGS+MOREARGS+2)	/* maximum number of usable tokens */
171#define	SCREENWIDTH  	78		/* nominal screen width in columns */
172
173/*
174 * Some variables used and manipulated locally
175 */
176static	struct timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
177static	struct timeval tvsout = { DEFSTIMEOUT, 0 };	/* secondary time out */
178static	l_fp delay_time;				/* delay time */
179static	char currenthost[LENHOSTNAME];			/* current host name */
180int showhostnames = 1;					/* show host names by default */
181
182static	int ai_fam_templ;				/* address family */
183static	int ai_fam_default;				/* default address family */
184static	SOCKET sockfd;					/* fd socket is opened on */
185static	int havehost = 0;				/* set to 1 when host open */
186int s_port = 0;
187
188#if defined (SYS_WINNT) || defined (SYS_VXWORKS)
189char password[9];
190#endif /* SYS_WINNT || SYS_VXWORKS */
191
192#ifdef SYS_WINNT
193DWORD NumberOfBytesWritten;
194
195HANDLE	TimerThreadHandle = NULL;	/* 1998/06/03 - Used in ntplib/machines.c */
196void timer(void)	{  ; };	/* 1998/06/03 - Used in ntplib/machines.c */
197
198#endif /* SYS_WINNT */
199
200/*
201 * Holds data returned from queries.  We allocate INITDATASIZE
202 * octets to begin with, increasing this as we need to.
203 */
204#define	INITDATASIZE	(sizeof(struct resp_pkt) * 16)
205#define	INCDATASIZE	(sizeof(struct resp_pkt) * 8)
206
207static	char *pktdata;
208static	int pktdatasize;
209
210/*
211 * These are used to help the magic with old and new versions of ntpd.
212 */
213int impl_ver = IMPL_XNTPD;
214static int req_pkt_size = REQ_LEN_NOMAC;
215
216/*
217 * For commands typed on the command line (with the -c option)
218 */
219static	int numcmds = 0;
220static	const char *ccmds[MAXCMDS];
221#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
222
223/*
224 * When multiple hosts are specified.
225 */
226static	int numhosts = 0;
227static	const char *chosts[MAXHOSTS];
228#define	ADDHOST(cp)	if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
229
230/*
231 * Error codes for internal use
232 */
233#define	ERR_INCOMPLETE		16
234#define	ERR_TIMEOUT		17
235
236/*
237 * Macro definitions we use
238 */
239#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
240#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
241#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
242
243/*
244 * For converting time stamps to dates
245 */
246#define	JAN_1970	2208988800	/* 1970 - 1900 in seconds */
247
248/*
249 * Jump buffer for longjumping back to the command level
250 */
251static	jmp_buf interrupt_buf;
252static  volatile int jump = 0;
253
254/*
255 * Pointer to current output unit
256 */
257static	FILE *current_output;
258
259/*
260 * Command table imported from ntpdc_ops.c
261 */
262extern struct xcmd opcmds[];
263
264char *progname;
265volatile int debug;
266
267#ifdef NO_MAIN_ALLOWED
268CALL(ntpdc,"ntpdc",ntpdcmain);
269#else
270int
271main(
272	int argc,
273	char *argv[]
274	)
275{
276	return ntpdcmain(argc, argv);
277}
278#endif
279
280#ifdef SYS_VXWORKS
281void clear_globals(void)
282{
283    showhostnames = 0;              /* show host names by default */
284    havehost = 0;                   /* set to 1 when host open */
285    numcmds = 0;
286    numhosts = 0;
287}
288#endif
289
290/*
291 * main - parse arguments and handle options
292 */
293int
294ntpdcmain(
295	int argc,
296	char *argv[]
297	)
298{
299	extern int ntp_optind;
300
301	delay_time.l_ui = 0;
302	delay_time.l_uf = DEFDELAY;
303
304#ifdef SYS_VXWORKS
305	clear_globals();
306	taskPrioritySet(taskIdSelf(), 100 );
307#endif
308
309#ifdef SYS_WINNT
310	if (!Win32InitSockets())
311	{
312		fprintf(stderr, "No useable winsock.dll:");
313		exit(1);
314	}
315#endif /* SYS_WINNT */
316
317	/* Check to see if we have IPv6. Otherwise force the -4 flag */
318	if (isc_net_probeipv6() != ISC_R_SUCCESS) {
319		ai_fam_default = AF_INET;
320	}
321
322	progname = argv[0];
323
324	{
325		int optct = optionProcess(&ntpdcOptions, argc, argv);
326		argc -= optct;
327		argv += optct;
328	}
329
330	switch (WHICH_IDX_IPV4) {
331	    case INDEX_OPT_IPV4:
332		ai_fam_templ = AF_INET;
333		break;
334	    case INDEX_OPT_IPV6:
335		ai_fam_templ = AF_INET6;
336		break;
337	    default:
338		ai_fam_templ = ai_fam_default;
339		break;
340	}
341
342	if (HAVE_OPT(COMMAND)) {
343		int		cmdct = STACKCT_OPT( COMMAND );
344		const char**	cmds  = STACKLST_OPT( COMMAND );
345
346		while (cmdct-- > 0) {
347			ADDCMD(*cmds++);
348		}
349	}
350
351	debug = DESC(DEBUG_LEVEL).optOccCt;
352
353	if (HAVE_OPT(INTERACTIVE)) {
354		interactive = 1;
355	}
356
357	if (HAVE_OPT(NUMERIC)) {
358		showhostnames = 0;
359	}
360
361	if (HAVE_OPT(LISTPEERS)) {
362		ADDCMD("listpeers");
363	}
364
365	if (HAVE_OPT(PEERS)) {
366		ADDCMD("peers");
367	}
368
369	if (HAVE_OPT(SHOWPEERS)) {
370		ADDCMD("dmpeers");
371	}
372
373	if (ntp_optind == argc) {
374		ADDHOST(DEFHOST);
375	} else {
376		for (; ntp_optind < argc; ntp_optind++)
377		    ADDHOST(argv[ntp_optind]);
378	}
379
380	if (numcmds == 0 && interactive == 0
381	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
382		interactive = 1;
383	}
384
385#if 0
386	ai_fam_templ = ai_fam_default;
387	while ((c = ntp_getopt(argc, argv, "46c:dilnps")) != EOF)
388	    switch (c) {
389		case '4':
390		    ai_fam_templ = AF_INET;
391		    break;
392		case '6':
393		    ai_fam_templ = AF_INET6;
394		    break;
395		case 'c':
396		    ADDCMD(ntp_optarg);
397		    break;
398		case 'd':
399		    ++debug;
400		    break;
401		case 'i':
402		    interactive = 1;
403		    break;
404		case 'l':
405		    ADDCMD("listpeers");
406		    break;
407		case 'n':
408		    showhostnames = 0;
409		    break;
410		case 'p':
411		    ADDCMD("peers");
412		    break;
413		case 's':
414		    ADDCMD("dmpeers");
415		    break;
416		default:
417		    errflg++;
418		    break;
419	    }
420
421	if (errflg) {
422		(void) fprintf(stderr,
423			       "usage: %s [-46dilnps] [-c cmd] host ...\n",
424			       progname);
425		exit(2);
426	}
427
428	if (ntp_optind == argc) {
429		ADDHOST(DEFHOST);
430	} else {
431		for (; ntp_optind < argc; ntp_optind++)
432		    ADDHOST(argv[ntp_optind]);
433	}
434
435	if (numcmds == 0 && interactive == 0
436	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
437		interactive = 1;
438	}
439#endif
440
441#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
442	if (interactive)
443	    (void) signal_no_reset(SIGINT, abortcmd);
444#endif /* SYS_WINNT */
445
446	/*
447	 * Initialize the packet data buffer
448	 */
449	pktdata = (char *)malloc(INITDATASIZE);
450	if (pktdata == NULL) {
451		(void) fprintf(stderr, "%s: malloc() failed!\n", progname);
452		exit(1);
453	}
454	pktdatasize = INITDATASIZE;
455
456	if (numcmds == 0) {
457		(void) openhost(chosts[0]);
458		getcmds();
459	} else {
460		int ihost;
461		int icmd;
462
463		for (ihost = 0; ihost < numhosts; ihost++) {
464			if (openhost(chosts[ihost]))
465			    for (icmd = 0; icmd < numcmds; icmd++) {
466				    if (numhosts > 1)
467					printf ("--- %s ---\n",chosts[ihost]);
468				    docmd(ccmds[icmd]);
469			    }
470		}
471	}
472#ifdef SYS_WINNT
473	WSACleanup();
474#endif
475	return(0);
476} /* main end */
477
478
479/*
480 * openhost - open a socket to a host
481 */
482static int
483openhost(
484	const char *hname
485	)
486{
487	char temphost[LENHOSTNAME];
488	int a_info, i;
489	struct addrinfo hints, *ai = NULL;
490	register const char *cp;
491	char name[LENHOSTNAME];
492	char service[5];
493
494	/*
495	 * We need to get by the [] if they were entered
496	 */
497
498	cp = hname;
499
500	if (*cp == '[') {
501		cp++;
502		for(i = 0; *cp != ']'; cp++, i++)
503			name[i] = *cp;
504		name[i] = '\0';
505		hname = name;
506	}
507
508	/*
509	 * First try to resolve it as an ip address and if that fails,
510	 * do a fullblown (dns) lookup. That way we only use the dns
511	 * when it is needed and work around some implementations that
512	 * will return an "IPv4-mapped IPv6 address" address if you
513	 * give it an IPv4 address to lookup.
514	 */
515	strcpy(service, "ntp");
516	memset((char *)&hints, 0, sizeof(struct addrinfo));
517	hints.ai_family = ai_fam_templ;
518	hints.ai_protocol = IPPROTO_UDP;
519	hints.ai_socktype = SOCK_DGRAM;
520	hints.ai_flags = AI_NUMERICHOST;
521
522	a_info = getaddrinfo(hname, service, &hints, &ai);
523	if (a_info == EAI_NONAME
524#ifdef EAI_NODATA
525	    || a_info == EAI_NODATA
526#endif
527	   ) {
528		hints.ai_flags = AI_CANONNAME;
529#ifdef AI_ADDRCONFIG
530		hints.ai_flags |= AI_ADDRCONFIG;
531#endif
532		a_info = getaddrinfo(hname, service, &hints, &ai);
533	}
534	/* Some older implementations don't like AI_ADDRCONFIG. */
535	if (a_info == EAI_BADFLAGS) {
536		hints.ai_flags = AI_CANONNAME;
537		a_info = getaddrinfo(hname, service, &hints, &ai);
538	}
539	if (a_info != 0) {
540		(void) fprintf(stderr, "%s\n", gai_strerror(a_info));
541		if (ai != NULL)
542			freeaddrinfo(ai);
543		return 0;
544	}
545
546	if (ai->ai_canonname == NULL) {
547		strncpy(temphost, stoa((struct sockaddr_storage *)ai->ai_addr),
548		    LENHOSTNAME);
549		temphost[LENHOSTNAME-1] = '\0';
550	} else {
551		strncpy(temphost, ai->ai_canonname, LENHOSTNAME);
552		temphost[LENHOSTNAME-1] = '\0';
553	}
554
555	if (debug > 2)
556	    printf("Opening host %s\n", temphost);
557
558	if (havehost == 1) {
559		if (debug > 2)
560		    printf("Closing old host %s\n", currenthost);
561		(void) closesocket(sockfd);
562		havehost = 0;
563	}
564	(void) strcpy(currenthost, temphost);
565
566	/* port maps to the same in both families */
567	s_port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port;
568#ifdef SYS_VXWORKS
569	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
570	if (ai->ai_family == AF_INET)
571		*(struct sockaddr_in *)&hostaddr=
572			*((struct sockaddr_in *)ai->ai_addr);
573	else
574		*(struct sockaddr_in6 *)&hostaddr=
575			*((struct sockaddr_in6 *)ai->ai_addr);
576#endif /* SYS_VXWORKS */
577
578#ifdef SYS_WINNT
579	{
580		int optionValue = SO_SYNCHRONOUS_NONALERT;
581		int err;
582
583		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue));
584		if (err != NO_ERROR) {
585			(void) fprintf(stderr, "cannot open nonoverlapped sockets\n");
586			exit(1);
587		}
588	}
589
590	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
591	if (sockfd == INVALID_SOCKET) {
592		error("socket", "", "");
593		exit(-1);
594	}
595#else
596	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
597	if (sockfd == -1)
598	    error("socket", "", "");
599#endif /* SYS_WINNT */
600
601
602#ifdef NEED_RCVBUF_SLOP
603# ifdef SO_RCVBUF
604	{
605		int rbufsize = INITDATASIZE + 2048; /* 2K for slop */
606
607		if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
608			       &rbufsize, sizeof(int)) == -1)
609		    error("setsockopt", "", "");
610	}
611# endif
612#endif
613
614#ifdef SYS_VXWORKS
615	if (connect(sockfd, (struct sockaddr *)&hostaddr,
616		    sizeof(hostaddr)) == -1)
617#else
618	if (connect(sockfd, (struct sockaddr *)ai->ai_addr,
619		    ai->ai_addrlen) == -1)
620#endif /* SYS_VXWORKS */
621	    error("connect", "", "");
622	if (ai != NULL)
623		freeaddrinfo(ai);
624	havehost = 1;
625	req_pkt_size = REQ_LEN_NOMAC;
626	impl_ver = IMPL_XNTPD;
627	return 1;
628}
629
630
631/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
632/*
633 * sendpkt - send a packet to the remote host
634 */
635static int
636sendpkt(
637	char *xdata,
638	int xdatalen
639	)
640{
641	if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) {
642		warning("write to %s failed", currenthost, "");
643		return -1;
644	}
645
646	return 0;
647}
648
649
650/*
651 * growpktdata - grow the packet data area
652 */
653static void
654growpktdata(void)
655{
656	pktdatasize += INCDATASIZE;
657	pktdata = (char *)realloc(pktdata, (unsigned)pktdatasize);
658	if (pktdata == 0) {
659		(void) fprintf(stderr, "%s: realloc() failed!\n", progname);
660		exit(1);
661	}
662}
663
664
665/*
666 * getresponse - get a (series of) response packet(s) and return the data
667 */
668static int
669getresponse(
670	int implcode,
671	int reqcode,
672	int *ritems,
673	int *rsize,
674	char **rdata,
675	int esize
676	)
677{
678	struct resp_pkt rpkt;
679	struct timeval tvo;
680	int items;
681	int i;
682	int size;
683	int datasize;
684	char *datap;
685	char *tmp_data;
686	char haveseq[MAXSEQ+1];
687	int firstpkt;
688	int lastseq;
689	int numrecv;
690	int seq;
691	fd_set fds;
692	int n;
693	int pad;
694
695	/*
696	 * This is pretty tricky.  We may get between 1 and many packets
697	 * back in response to the request.  We peel the data out of
698	 * each packet and collect it in one long block.  When the last
699	 * packet in the sequence is received we'll know how many we
700	 * should have had.  Note we use one long time out, should reconsider.
701	 */
702	*ritems = 0;
703	*rsize = 0;
704	firstpkt = 1;
705	numrecv = 0;
706	*rdata = datap = pktdata;
707	lastseq = 999;	/* too big to be a sequence number */
708	memset(haveseq, 0, sizeof(haveseq));
709	FD_ZERO(&fds);
710
711    again:
712	if (firstpkt)
713	    tvo = tvout;
714	else
715	    tvo = tvsout;
716
717	FD_SET(sockfd, &fds);
718	n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
719
720	if (n == -1) {
721		warning("select fails", "", "");
722		return -1;
723	}
724	if (n == 0) {
725		/*
726		 * Timed out.  Return what we have
727		 */
728		if (firstpkt) {
729			(void) fprintf(stderr,
730				       "%s: timed out, nothing received\n", currenthost);
731			return ERR_TIMEOUT;
732		} else {
733			(void) fprintf(stderr,
734				       "%s: timed out with incomplete data\n",
735				       currenthost);
736			if (debug) {
737				printf("Received sequence numbers");
738				for (n = 0; n <= MAXSEQ; n++)
739				    if (haveseq[n])
740					printf(" %d,", n);
741				if (lastseq != 999)
742				    printf(" last frame received\n");
743				else
744				    printf(" last frame not received\n");
745			}
746			return ERR_INCOMPLETE;
747		}
748	}
749
750	n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
751	if (n == -1) {
752		warning("read", "", "");
753		return -1;
754	}
755
756
757	/*
758	 * Check for format errors.  Bug proofing.
759	 */
760	if (n < RESP_HEADER_SIZE) {
761		if (debug)
762		    printf("Short (%d byte) packet received\n", n);
763		goto again;
764	}
765	if (INFO_VERSION(rpkt.rm_vn_mode) > NTP_VERSION ||
766	    INFO_VERSION(rpkt.rm_vn_mode) < NTP_OLDVERSION) {
767		if (debug)
768		    printf("Packet received with version %d\n",
769			   INFO_VERSION(rpkt.rm_vn_mode));
770		goto again;
771	}
772	if (INFO_MODE(rpkt.rm_vn_mode) != MODE_PRIVATE) {
773		if (debug)
774		    printf("Packet received with mode %d\n",
775			   INFO_MODE(rpkt.rm_vn_mode));
776		goto again;
777	}
778	if (INFO_IS_AUTH(rpkt.auth_seq)) {
779		if (debug)
780		    printf("Encrypted packet received\n");
781		goto again;
782	}
783	if (!ISRESPONSE(rpkt.rm_vn_mode)) {
784		if (debug)
785		    printf("Received request packet, wanted response\n");
786		goto again;
787	}
788	if (INFO_MBZ(rpkt.mbz_itemsize) != 0) {
789		if (debug)
790		    printf("Received packet with nonzero MBZ field!\n");
791		goto again;
792	}
793
794	/*
795	 * Check implementation/request.  Could be old data getting to us.
796	 */
797	if (rpkt.implementation != implcode || rpkt.request != reqcode) {
798		if (debug)
799		    printf(
800			    "Received implementation/request of %d/%d, wanted %d/%d",
801			    rpkt.implementation, rpkt.request,
802			    implcode, reqcode);
803		goto again;
804	}
805
806	/*
807	 * Check the error code.  If non-zero, return it.
808	 */
809	if (INFO_ERR(rpkt.err_nitems) != INFO_OKAY) {
810		if (debug && ISMORE(rpkt.rm_vn_mode)) {
811			printf("Error code %d received on not-final packet\n",
812			       INFO_ERR(rpkt.err_nitems));
813		}
814		return (int)INFO_ERR(rpkt.err_nitems);
815	}
816
817	/*
818	 * Collect items and size.  Make sure they make sense.
819	 */
820	items = INFO_NITEMS(rpkt.err_nitems);
821	size = INFO_ITEMSIZE(rpkt.mbz_itemsize);
822	if (esize > size)
823		pad = esize - size;
824	else
825		pad = 0;
826	if ((datasize = items*size) > (n-RESP_HEADER_SIZE)) {
827		if (debug)
828		    printf(
829			    "Received items %d, size %d (total %d), data in packet is %d\n",
830			    items, size, datasize, n-RESP_HEADER_SIZE);
831		goto again;
832	}
833
834	/*
835	 * If this isn't our first packet, make sure the size matches
836	 * the other ones.
837	 */
838	if (!firstpkt && esize != *rsize) {
839		if (debug)
840		    printf("Received itemsize %d, previous %d\n",
841			   size, *rsize);
842		goto again;
843	}
844	/*
845	 * If we've received this before, +toss it
846	 */
847	seq = INFO_SEQ(rpkt.auth_seq);
848	if (haveseq[seq]) {
849		if (debug)
850		    printf("Received duplicate sequence number %d\n", seq);
851		goto again;
852	}
853	haveseq[seq] = 1;
854
855	/*
856	 * If this is the last in the sequence, record that.
857	 */
858	if (!ISMORE(rpkt.rm_vn_mode)) {
859		if (lastseq != 999) {
860			printf("Received second end sequence packet\n");
861			goto again;
862		}
863		lastseq = seq;
864	}
865
866	/*
867	 * So far, so good.  Copy this data into the output array.
868	 */
869	if ((datap + datasize + (pad * items)) > (pktdata + pktdatasize)) {
870		int offset = datap - pktdata;
871		growpktdata();
872	        *rdata = pktdata; /* might have been realloced ! */
873		datap = pktdata + offset;
874	}
875	/*
876	 * We now move the pointer along according to size and number of
877	 * items.  This is so we can play nice with older implementations
878	 */
879
880	tmp_data = (char *)rpkt.data;
881	for(i = 0; i <items; i++){
882		memmove(datap, tmp_data, (unsigned)size);
883		tmp_data += size;
884		memset(datap + size, 0, pad);
885		datap += size + pad;
886	}
887
888	if (firstpkt) {
889		firstpkt = 0;
890		*rsize = size + pad;
891	}
892	*ritems += items;
893
894	/*
895	 * Finally, check the count of received packets.  If we've got them
896	 * all, return
897	 */
898	++numrecv;
899	if (numrecv <= lastseq)
900	    goto again;
901	return INFO_OKAY;
902}
903
904
905/*
906 * sendrequest - format and send a request packet
907 */
908static int
909sendrequest(
910	int implcode,
911	int reqcode,
912	int auth,
913	int qitems,
914	int qsize,
915	char *qdata
916	)
917{
918	struct req_pkt qpkt;
919	int datasize;
920
921	memset((char *)&qpkt, 0, sizeof qpkt);
922
923	qpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
924	qpkt.implementation = (u_char)implcode;
925	qpkt.request = (u_char)reqcode;
926
927	datasize = qitems * qsize;
928	if (datasize != 0 && qdata != NULL) {
929		memmove((char *)qpkt.data, qdata, (unsigned)datasize);
930		qpkt.err_nitems = ERR_NITEMS(0, qitems);
931		qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);
932	} else {
933		qpkt.err_nitems = ERR_NITEMS(0, 0);
934		qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);  /* allow for optional first item */
935	}
936
937	if (!auth || (keyid_entered && info_auth_keyid == 0)) {
938		qpkt.auth_seq = AUTH_SEQ(0, 0);
939		return sendpkt((char *)&qpkt, req_pkt_size);
940	} else {
941		l_fp ts;
942		int maclen = 0;
943		const char *pass = "\0";
944		struct req_pkt_tail *qpktail;
945
946		qpktail = (struct req_pkt_tail *)((char *)&qpkt + req_pkt_size
947		    + MAX_MAC_LEN - sizeof(struct req_pkt_tail));
948
949		if (info_auth_keyid == 0) {
950			if (((struct conf_peer *)qpkt.data)->keyid > 0)
951				info_auth_keyid = ((struct conf_peer *)qpkt.data)->keyid;
952			else {
953				maclen = getkeyid("Keyid: ");
954				if (maclen == 0) {
955					(void) fprintf(stderr,
956					    "Invalid key identifier\n");
957					return 1;
958				}
959				info_auth_keyid = maclen;
960			}
961		}
962		if (!authistrusted(info_auth_keyid)) {
963			pass = getpass("MD5 Password: ");
964			if (*pass == '\0') {
965				(void) fprintf(stderr,
966				    "Invalid password\n");
967				return (1);
968			}
969		}
970		authusekey(info_auth_keyid, info_auth_keytype, (const u_char *)pass);
971		authtrust(info_auth_keyid, 1);
972		qpkt.auth_seq = AUTH_SEQ(1, 0);
973		qpktail->keyid = htonl(info_auth_keyid);
974		get_systime(&ts);
975		L_ADD(&ts, &delay_time);
976		HTONL_FP(&ts, &qpktail->tstamp);
977		maclen = authencrypt(info_auth_keyid, (u_int32 *)&qpkt,
978		    req_pkt_size);
979		if (maclen == 0) {
980			(void) fprintf(stderr, "Key not found\n");
981			return (1);
982		}
983		return sendpkt((char *)&qpkt, (int)(req_pkt_size + maclen));
984	}
985	/*NOTREACHED*/
986}
987
988
989/*
990 * doquery - send a request and process the response
991 */
992int
993doquery(
994	int implcode,
995	int reqcode,
996	int auth,
997	int qitems,
998	int qsize,
999	char *qdata,
1000	int *ritems,
1001	int *rsize,
1002	char **rdata,
1003 	int quiet_mask,
1004	int esize
1005	)
1006{
1007	int res;
1008	char junk[512];
1009	fd_set fds;
1010	struct timeval tvzero;
1011
1012	/*
1013	 * Check to make sure host is open
1014	 */
1015	if (!havehost) {
1016		(void) fprintf(stderr, "***No host open, use `host' command\n");
1017		return -1;
1018	}
1019
1020	/*
1021	 * Poll the socket and clear out any pending data
1022	 */
1023again:
1024	do {
1025		tvzero.tv_sec = tvzero.tv_usec = 0;
1026		FD_ZERO(&fds);
1027		FD_SET(sockfd, &fds);
1028		res = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
1029
1030		if (res == -1) {
1031			warning("polling select", "", "");
1032			return -1;
1033		} else if (res > 0)
1034
1035		    (void) recv(sockfd, junk, sizeof junk, 0);
1036	} while (res > 0);
1037
1038
1039	/*
1040	 * send a request
1041	 */
1042	res = sendrequest(implcode, reqcode, auth, qitems, qsize, qdata);
1043	if (res != 0)
1044	    return res;
1045
1046	/*
1047	 * Get the response.  If we got a standard error, print a message
1048	 */
1049	res = getresponse(implcode, reqcode, ritems, rsize, rdata, esize);
1050
1051	/*
1052	 * Try to be compatible with older implementations of ntpd.
1053	 */
1054	if (res == INFO_ERR_FMT && req_pkt_size != 48) {
1055		int oldsize;
1056
1057		oldsize = req_pkt_size;
1058
1059		switch(req_pkt_size) {
1060		case REQ_LEN_NOMAC:
1061			req_pkt_size = 160;
1062			break;
1063		case 160:
1064			req_pkt_size = 48;
1065			break;
1066		}
1067		if (impl_ver == IMPL_XNTPD) {
1068			fprintf(stderr,
1069			    "***Warning changing to older implementation\n");
1070			return INFO_ERR_IMPL;
1071		}
1072
1073		fprintf(stderr,
1074		    "***Warning changing the request packet size from %d to %d\n",
1075		    oldsize, req_pkt_size);
1076		goto again;
1077	}
1078
1079 	/* log error message if not told to be quiet */
1080 	if ((res > 0) && (((1 << res) & quiet_mask) == 0)) {
1081		switch(res) {
1082		    case INFO_ERR_IMPL:
1083			/* Give us a chance to try the older implementation. */
1084			if (implcode == IMPL_XNTPD)
1085				break;
1086			(void) fprintf(stderr,
1087				       "***Server implementation incompatable with our own\n");
1088			break;
1089		    case INFO_ERR_REQ:
1090			(void) fprintf(stderr,
1091				       "***Server doesn't implement this request\n");
1092			break;
1093		    case INFO_ERR_FMT:
1094			(void) fprintf(stderr,
1095				       "***Server reports a format error in the received packet (shouldn't happen)\n");
1096			break;
1097		    case INFO_ERR_NODATA:
1098			(void) fprintf(stderr,
1099				       "***Server reports data not found\n");
1100			break;
1101		    case INFO_ERR_AUTH:
1102			(void) fprintf(stderr, "***Permission denied\n");
1103			break;
1104		    case ERR_TIMEOUT:
1105			(void) fprintf(stderr, "***Request timed out\n");
1106			break;
1107		    case ERR_INCOMPLETE:
1108			(void) fprintf(stderr,
1109				       "***Response from server was incomplete\n");
1110			break;
1111		    default:
1112			(void) fprintf(stderr,
1113				       "***Server returns unknown error code %d\n", res);
1114			break;
1115		}
1116	}
1117	return res;
1118}
1119
1120
1121/*
1122 * getcmds - read commands from the standard input and execute them
1123 */
1124static void
1125getcmds(void)
1126{
1127#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
1128	char *line;
1129
1130	for (;;) {
1131		if ((line = readline(interactive?prompt:"")) == NULL) return;
1132		if (*line) add_history(line);
1133		docmd(line);
1134		free(line);
1135	}
1136#else /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */
1137	char line[MAXLINE];
1138
1139	for (;;) {
1140		if (interactive) {
1141#ifdef VMS	/* work around a problem with mixing stdout & stderr */
1142			fputs("",stdout);
1143#endif
1144			(void) fputs(prompt, stderr);
1145			(void) fflush(stderr);
1146		}
1147
1148		if (fgets(line, sizeof line, stdin) == NULL)
1149		    return;
1150
1151		docmd(line);
1152	}
1153#endif /* not HAVE_LIBREADLINE || HAVE_LIBEDIT */
1154}
1155
1156
1157#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
1158/*
1159 * abortcmd - catch interrupts and abort the current command
1160 */
1161static RETSIGTYPE
1162abortcmd(
1163	int sig
1164	)
1165{
1166
1167	if (current_output == stdout)
1168	    (void) fflush(stdout);
1169	putc('\n', stderr);
1170	(void) fflush(stderr);
1171	if (jump) longjmp(interrupt_buf, 1);
1172}
1173#endif /* SYS_WINNT */
1174
1175/*
1176 * docmd - decode the command line and execute a command
1177 */
1178static void
1179docmd(
1180	const char *cmdline
1181	)
1182{
1183	char *tokens[1+MAXARGS+MOREARGS+2];
1184	struct parse pcmd;
1185	int ntok;
1186	int i, ti;
1187	int rval;
1188	struct xcmd *xcmd;
1189
1190	ai_fam_templ = ai_fam_default;
1191	/*
1192	 * Tokenize the command line.  If nothing on it, return.
1193	 */
1194	tokenize(cmdline, tokens, &ntok);
1195	if (ntok == 0)
1196	    return;
1197
1198	/*
1199	 * Find the appropriate command description.
1200	 */
1201	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1202	if (i == 0) {
1203		(void) fprintf(stderr, "***Command `%s' unknown\n",
1204			       tokens[0]);
1205		return;
1206	} else if (i >= 2) {
1207		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
1208			       tokens[0]);
1209		return;
1210	}
1211
1212	/*
1213	 * Save the keyword, then walk through the arguments, interpreting
1214	 * as we go.
1215	 */
1216	pcmd.keyword = tokens[0];
1217	pcmd.nargs = 0;
1218	ti = 1;
1219	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO;) {
1220		if ((i+ti) >= ntok) {
1221			if (!(xcmd->arg[i] & OPT)) {
1222				printusage(xcmd, stderr);
1223				return;
1224			}
1225			break;
1226		}
1227		if ((xcmd->arg[i] & OPT) && (*tokens[i+ti] == '>'))
1228			break;
1229		rval = getarg(tokens[i+ti], (int)xcmd->arg[i], &pcmd.argval[i]);
1230		if (rval == -1) {
1231			ti++;
1232			continue;
1233		}
1234		if (rval == 0)
1235			return;
1236		pcmd.nargs++;
1237		i++;
1238	}
1239
1240	/* Any extra args are assumed to be "OPT|NTP_STR". */
1241	for ( ; i < MAXARGS + MOREARGS;) {
1242	     if ((i+ti) >= ntok)
1243		  break;
1244		rval = getarg(tokens[i+ti], (int)(OPT|NTP_STR), &pcmd.argval[i]);
1245		if (rval == -1) {
1246			ti++;
1247			continue;
1248		}
1249		if (rval == 0)
1250			return;
1251		pcmd.nargs++;
1252		i++;
1253	}
1254
1255	i += ti;
1256	if (i < ntok && *tokens[i] == '>') {
1257		char *fname;
1258
1259		if (*(tokens[i]+1) != '\0')
1260		    fname = tokens[i]+1;
1261		else if ((i+1) < ntok)
1262		    fname = tokens[i+1];
1263		else {
1264			(void) fprintf(stderr, "***No file for redirect\n");
1265			return;
1266		}
1267
1268		current_output = fopen(fname, "w");
1269		if (current_output == NULL) {
1270			(void) fprintf(stderr, "***Error opening %s: ", fname);
1271			perror("");
1272			return;
1273		}
1274	} else {
1275		current_output = stdout;
1276	}
1277
1278	if (interactive && setjmp(interrupt_buf)) {
1279		return;
1280	} else {
1281		jump = 1;
1282		(xcmd->handler)(&pcmd, current_output);
1283		jump = 0;
1284		if (current_output != stdout)
1285			(void) fclose(current_output);
1286		current_output = NULL;
1287	}
1288}
1289
1290
1291/*
1292 * tokenize - turn a command line into tokens
1293 */
1294static void
1295tokenize(
1296	const char *line,
1297	char **tokens,
1298	int *ntok
1299	)
1300{
1301	register const char *cp;
1302	register char *sp;
1303	static char tspace[MAXLINE];
1304
1305	sp = tspace;
1306	cp = line;
1307	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1308		tokens[*ntok] = sp;
1309		while (ISSPACE(*cp))
1310		    cp++;
1311		if (ISEOL(*cp))
1312		    break;
1313		do {
1314			*sp++ = *cp++;
1315		} while (!ISSPACE(*cp) && !ISEOL(*cp));
1316
1317		*sp++ = '\0';
1318	}
1319}
1320
1321
1322
1323/*
1324 * findcmd - find a command in a command description table
1325 */
1326static int
1327findcmd(
1328	register char *str,
1329	struct xcmd *clist1,
1330	struct xcmd *clist2,
1331	struct xcmd **cmd
1332	)
1333{
1334	register struct xcmd *cl;
1335	register int clen;
1336	int nmatch;
1337	struct xcmd *nearmatch = NULL;
1338	struct xcmd *clist;
1339
1340	clen = strlen(str);
1341	nmatch = 0;
1342	if (clist1 != 0)
1343	    clist = clist1;
1344	else if (clist2 != 0)
1345	    clist = clist2;
1346	else
1347	    return 0;
1348
1349    again:
1350	for (cl = clist; cl->keyword != 0; cl++) {
1351		/* do a first character check, for efficiency */
1352		if (*str != *(cl->keyword))
1353		    continue;
1354		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1355			/*
1356			 * Could be extact match, could be approximate.
1357			 * Is exact if the length of the keyword is the
1358			 * same as the str.
1359			 */
1360			if (*((cl->keyword) + clen) == '\0') {
1361				*cmd = cl;
1362				return 1;
1363			}
1364			nmatch++;
1365			nearmatch = cl;
1366		}
1367	}
1368
1369				/*
1370				 * See if there is more to do.  If so, go again.  Sorry about the
1371				 * goto, too much looking at BSD sources...
1372				 */
1373	if (clist == clist1 && clist2 != 0) {
1374		clist = clist2;
1375		goto again;
1376	}
1377
1378				/*
1379				 * If we got extactly 1 near match, use it, else return number
1380				 * of matches.
1381				 */
1382	if (nmatch == 1) {
1383		*cmd = nearmatch;
1384		return 1;
1385	}
1386	return nmatch;
1387}
1388
1389
1390/*
1391 * getarg - interpret an argument token
1392 *
1393 * string is always set.
1394 * type is set to the decoded type.
1395 *
1396 * return:	 0 - failure
1397 *		 1 - success
1398 *		-1 - skip to next token
1399 */
1400static int
1401getarg(
1402	char *str,
1403	int code,
1404	arg_v *argp
1405	)
1406{
1407	int isneg;
1408	char *cp, *np;
1409	static const char *digits = "0123456789";
1410
1411	memset(argp, 0, sizeof(*argp));
1412
1413	argp->string = str;
1414	argp->type   = code & ~OPT;
1415
1416	switch (argp->type) {
1417	    case NTP_STR:
1418		break;
1419	    case NTP_ADD:
1420		if (!strcmp("-6", str)) {
1421			ai_fam_templ = AF_INET6;
1422			return -1;
1423		} else if (!strcmp("-4", str)) {
1424			ai_fam_templ = AF_INET;
1425			return -1;
1426		}
1427		if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) {
1428			return 0;
1429		}
1430		break;
1431	    case NTP_INT:
1432	    case NTP_UINT:
1433		isneg = 0;
1434		np = str;
1435		if (*np == '-') {
1436			np++;
1437			isneg = 1;
1438		}
1439
1440		argp->uval = 0;
1441		do {
1442			cp = strchr(digits, *np);
1443			if (cp == NULL) {
1444				(void) fprintf(stderr,
1445					       "***Illegal integer value %s\n", str);
1446				return 0;
1447			}
1448			argp->uval *= 10;
1449			argp->uval += (cp - digits);
1450		} while (*(++np) != '\0');
1451
1452		if (isneg) {
1453			if ((code & ~OPT) == NTP_UINT) {
1454				(void) fprintf(stderr,
1455					       "***Value %s should be unsigned\n", str);
1456				return 0;
1457			}
1458			argp->ival = -argp->ival;
1459		}
1460		break;
1461	    case IP_VERSION:
1462		if (!strcmp("-6", str))
1463			argp->ival = 6 ;
1464		else if (!strcmp("-4", str))
1465			argp->ival = 4 ;
1466		else {
1467			(void) fprintf(stderr,
1468			    "***Version must be either 4 or 6\n");
1469			return 0;
1470		}
1471		break;
1472	}
1473
1474	return 1;
1475}
1476
1477
1478/*
1479 * getnetnum - given a host name, return its net number
1480 *	       and (optional) full name
1481 */
1482static int
1483getnetnum(
1484	const char *hname,
1485	struct sockaddr_storage *num,
1486	char *fullhost,
1487	int af
1488	)
1489{
1490	int sockaddr_len;
1491	struct addrinfo hints, *ai = NULL;
1492
1493	sockaddr_len = (af == AF_INET)
1494			   ? sizeof(struct sockaddr_in)
1495			   : sizeof(struct sockaddr_in6);
1496	memset((char *)&hints, 0, sizeof(struct addrinfo));
1497	hints.ai_flags = AI_CANONNAME;
1498#ifdef AI_ADDRCONFIG
1499	hints.ai_flags |= AI_ADDRCONFIG;
1500#endif
1501
1502	/* decodenetnum only works with addresses */
1503	if (decodenetnum(hname, num)) {
1504		if (fullhost != 0) {
1505			getnameinfo((struct sockaddr *)num, sockaddr_len,
1506				    fullhost, sizeof(fullhost), NULL, 0,
1507				    NI_NUMERICHOST);
1508		}
1509		return 1;
1510	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1511		memmove((char *)num, ai->ai_addr, ai->ai_addrlen);
1512		if (fullhost != 0)
1513			(void) strcpy(fullhost, ai->ai_canonname);
1514		return 1;
1515	} else {
1516		(void) fprintf(stderr, "***Can't find host %s\n", hname);
1517		return 0;
1518	}
1519	/*NOTREACHED*/
1520}
1521
1522/*
1523 * nntohost - convert network number to host name.  This routine enforces
1524 *	       the showhostnames setting.
1525 */
1526char *
1527nntohost(
1528	struct sockaddr_storage *netnum
1529	)
1530{
1531	if (!showhostnames)
1532	    return stoa(netnum);
1533
1534	if ((netnum->ss_family == AF_INET) && ISREFCLOCKADR(netnum))
1535		return refnumtoa(netnum);
1536	return socktohost(netnum);
1537}
1538
1539
1540/*
1541 * Finally, the built in command handlers
1542 */
1543
1544/*
1545 * help - tell about commands, or details of a particular command
1546 */
1547static void
1548help(
1549	struct parse *pcmd,
1550	FILE *fp
1551	)
1552{
1553	struct xcmd *xcp;
1554	char *cmd;
1555	const char *list[100];
1556	int word, words;
1557        int row, rows;
1558	int col, cols;
1559
1560	if (pcmd->nargs == 0) {
1561		words = 0;
1562		for (xcp = builtins; xcp->keyword != 0; xcp++) {
1563			if (*(xcp->keyword) != '?')
1564			    list[words++] = xcp->keyword;
1565		}
1566                for (xcp = opcmds; xcp->keyword != 0; xcp++)
1567		    list[words++] = xcp->keyword;
1568
1569		qsort(
1570#ifdef QSORT_USES_VOID_P
1571		    (void *)
1572#else
1573		    (char *)
1574#endif
1575			(list), (size_t)(words), sizeof(char *), helpsort);
1576		col = 0;
1577		for (word = 0; word < words; word++) {
1578			int length = strlen(list[word]);
1579			if (col < length) {
1580			    col = length;
1581                        }
1582		}
1583
1584		cols = SCREENWIDTH / ++col;
1585                rows = (words + cols - 1) / cols;
1586
1587		(void) fprintf(fp, "ntpdc commands:\n");
1588
1589		for (row = 0; row < rows; row++) {
1590                        for (word = row; word < words; word += rows) {
1591				(void) fprintf(fp, "%-*.*s", col, col-1, list[word]);
1592                        }
1593			(void) fprintf(fp, "\n");
1594		}
1595	} else {
1596		cmd = pcmd->argval[0].string;
1597		words = findcmd(cmd, builtins, opcmds, &xcp);
1598		if (words == 0) {
1599			(void) fprintf(stderr,
1600				       "Command `%s' is unknown\n", cmd);
1601			return;
1602		} else if (words >= 2) {
1603			(void) fprintf(stderr,
1604				       "Command `%s' is ambiguous\n", cmd);
1605			return;
1606		}
1607		(void) fprintf(fp, "function: %s\n", xcp->comment);
1608		printusage(xcp, fp);
1609	}
1610}
1611
1612
1613/*
1614 * helpsort - do hostname qsort comparisons
1615 */
1616#ifdef QSORT_USES_VOID_P
1617static int
1618helpsort(
1619	const void *t1,
1620	const void *t2
1621	)
1622{
1623	char const * const * name1 = (char const * const *)t1;
1624	char const * const * name2 = (char const * const *)t2;
1625
1626	return strcmp(*name1, *name2);
1627}
1628#else
1629static int
1630helpsort(
1631	char **name1,
1632	char **name2
1633	)
1634{
1635	return strcmp(*name1, *name2);
1636}
1637#endif
1638
1639
1640/*
1641 * printusage - print usage information for a command
1642 */
1643static void
1644printusage(
1645	struct xcmd *xcp,
1646	FILE *fp
1647	)
1648{
1649	int i, opt46;
1650
1651	opt46 = 0;
1652	(void) fprintf(fp, "usage: %s", xcp->keyword);
1653	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
1654		if (opt46 == 0 && (xcp->arg[i] & ~OPT) == NTP_ADD) {
1655			(void) fprintf(fp, " [ -4|-6 ]");
1656			opt46 = 1;
1657		}
1658		if (xcp->arg[i] & OPT)
1659		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
1660		else
1661		    (void) fprintf(fp, " %s", xcp->desc[i]);
1662	}
1663	(void) fprintf(fp, "\n");
1664}
1665
1666
1667/*
1668 * timeout - set time out time
1669 */
1670static void
1671timeout(
1672	struct parse *pcmd,
1673	FILE *fp
1674	)
1675{
1676	int val;
1677
1678	if (pcmd->nargs == 0) {
1679		val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
1680		(void) fprintf(fp, "primary timeout %d ms\n", val);
1681	} else {
1682		tvout.tv_sec = pcmd->argval[0].uval / 1000;
1683		tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
1684			* 1000;
1685	}
1686}
1687
1688
1689/*
1690 * my_delay - set delay for auth requests
1691 */
1692static void
1693my_delay(
1694	struct parse *pcmd,
1695	FILE *fp
1696	)
1697{
1698	int isneg;
1699	u_long val;
1700
1701	if (pcmd->nargs == 0) {
1702		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
1703		(void) fprintf(fp, "delay %lu ms\n", val);
1704	} else {
1705		if (pcmd->argval[0].ival < 0) {
1706			isneg = 1;
1707			val = (u_long)(-pcmd->argval[0].ival);
1708		} else {
1709			isneg = 0;
1710			val = (u_long)pcmd->argval[0].ival;
1711		}
1712
1713		delay_time.l_ui = val / 1000;
1714		val %= 1000;
1715		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
1716
1717		if (isneg)
1718		    L_NEG(&delay_time);
1719	}
1720}
1721
1722
1723/*
1724 * host - set the host we are dealing with.
1725 */
1726static void
1727host(
1728	struct parse *pcmd,
1729	FILE *fp
1730	)
1731{
1732	int i;
1733
1734	if (pcmd->nargs == 0) {
1735		if (havehost)
1736		    (void) fprintf(fp, "current host is %s\n", currenthost);
1737		else
1738		    (void) fprintf(fp, "no current host\n");
1739		return;
1740	}
1741
1742	i = 0;
1743	if (pcmd->nargs == 2) {
1744		if (!strcmp("-4", pcmd->argval[i].string))
1745			ai_fam_templ = AF_INET;
1746		else if (!strcmp("-6", pcmd->argval[i].string))
1747			ai_fam_templ = AF_INET6;
1748		else {
1749			if (havehost)
1750				(void) fprintf(fp,
1751				    "current host remains %s\n", currenthost);
1752			else
1753				(void) fprintf(fp, "still no current host\n");
1754			return;
1755		}
1756		i = 1;
1757	}
1758	if (openhost(pcmd->argval[i].string)) {
1759		(void) fprintf(fp, "current host set to %s\n", currenthost);
1760	} else {
1761		if (havehost)
1762		    (void) fprintf(fp,
1763				   "current host remains %s\n", currenthost);
1764		else
1765		    (void) fprintf(fp, "still no current host\n");
1766	}
1767}
1768
1769
1770/*
1771 * keyid - get a keyid to use for authenticating requests
1772 */
1773static void
1774keyid(
1775	struct parse *pcmd,
1776	FILE *fp
1777	)
1778{
1779	if (pcmd->nargs == 0) {
1780		if (info_auth_keyid == 0 && !keyid_entered)
1781		    (void) fprintf(fp, "no keyid defined\n");
1782		else if (info_auth_keyid == 0 && keyid_entered)
1783		    (void) fprintf(fp, "no keyid will be sent\n");
1784		else
1785		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
1786	} else {
1787		info_auth_keyid = pcmd->argval[0].uval;
1788		keyid_entered = 1;
1789	}
1790}
1791
1792
1793/*
1794 * keytype - get type of key to use for authenticating requests
1795 */
1796static void
1797keytype(
1798	struct parse *pcmd,
1799	FILE *fp
1800	)
1801{
1802	if (pcmd->nargs == 0)
1803	    fprintf(fp, "keytype is %s\n",
1804		    (info_auth_keytype == KEY_TYPE_MD5) ? "MD5" : "???");
1805	else
1806	    switch (*(pcmd->argval[0].string)) {
1807		case 'm':
1808		case 'M':
1809		    info_auth_keytype = KEY_TYPE_MD5;
1810		    break;
1811
1812		default:
1813		    fprintf(fp, "keytype must be 'md5'\n");
1814	    }
1815}
1816
1817
1818
1819/*
1820 * passwd - get an authentication key
1821 */
1822/*ARGSUSED*/
1823static void
1824passwd(
1825	struct parse *pcmd,
1826	FILE *fp
1827	)
1828{
1829	char *pass;
1830
1831	if (info_auth_keyid == 0) {
1832		info_auth_keyid = getkeyid("Keyid: ");
1833		if (info_auth_keyid == 0) {
1834			(void)fprintf(fp, "Keyid must be defined\n");
1835			return;
1836		}
1837	}
1838	if (!interactive) {
1839		authusekey(info_auth_keyid, info_auth_keytype,
1840			   (u_char *)pcmd->argval[0].string);
1841		authtrust(info_auth_keyid, 1);
1842	} else {
1843		pass = getpass("MD5 Password: ");
1844		if (*pass == '\0')
1845		    (void) fprintf(fp, "Password unchanged\n");
1846		else {
1847		    authusekey(info_auth_keyid, info_auth_keytype,
1848			       (u_char *)pass);
1849		    authtrust(info_auth_keyid, 1);
1850		}
1851	}
1852}
1853
1854
1855/*
1856 * hostnames - set the showhostnames flag
1857 */
1858static void
1859hostnames(
1860	struct parse *pcmd,
1861	FILE *fp
1862	)
1863{
1864	if (pcmd->nargs == 0) {
1865		if (showhostnames)
1866		    (void) fprintf(fp, "hostnames being shown\n");
1867		else
1868		    (void) fprintf(fp, "hostnames not being shown\n");
1869	} else {
1870		if (STREQ(pcmd->argval[0].string, "yes"))
1871		    showhostnames = 1;
1872		else if (STREQ(pcmd->argval[0].string, "no"))
1873		    showhostnames = 0;
1874		else
1875		    (void)fprintf(stderr, "What?\n");
1876	}
1877}
1878
1879
1880/*
1881 * setdebug - set/change debugging level
1882 */
1883static void
1884setdebug(
1885	struct parse *pcmd,
1886	FILE *fp
1887	)
1888{
1889	if (pcmd->nargs == 0) {
1890		(void) fprintf(fp, "debug level is %d\n", debug);
1891		return;
1892	} else if (STREQ(pcmd->argval[0].string, "no")) {
1893		debug = 0;
1894	} else if (STREQ(pcmd->argval[0].string, "more")) {
1895		debug++;
1896	} else if (STREQ(pcmd->argval[0].string, "less")) {
1897		debug--;
1898	} else {
1899		(void) fprintf(fp, "What?\n");
1900		return;
1901	}
1902	(void) fprintf(fp, "debug level set to %d\n", debug);
1903}
1904
1905
1906/*
1907 * quit - stop this nonsense
1908 */
1909/*ARGSUSED*/
1910static void
1911quit(
1912	struct parse *pcmd,
1913	FILE *fp
1914	)
1915{
1916	if (havehost)
1917	    closesocket(sockfd);
1918	exit(0);
1919}
1920
1921
1922/*
1923 * version - print the current version number
1924 */
1925/*ARGSUSED*/
1926static void
1927version(
1928	struct parse *pcmd,
1929	FILE *fp
1930	)
1931{
1932
1933	(void) fprintf(fp, "%s\n", Version);
1934	return;
1935}
1936
1937
1938/*
1939 * warning - print a warning message
1940 */
1941static void
1942warning(
1943	const char *fmt,
1944	const char *st1,
1945	const char *st2
1946	)
1947{
1948	(void) fprintf(stderr, "%s: ", progname);
1949	(void) fprintf(stderr, fmt, st1, st2);
1950	(void) fprintf(stderr, ": ");
1951	perror("");
1952}
1953
1954
1955/*
1956 * error - print a message and exit
1957 */
1958static void
1959error(
1960	const char *fmt,
1961	const char *st1,
1962	const char *st2
1963	)
1964{
1965	warning(fmt, st1, st2);
1966	exit(1);
1967}
1968
1969/*
1970 * getkeyid - prompt the user for a keyid to use
1971 */
1972static u_long
1973getkeyid(
1974	const char *keyprompt
1975	)
1976{
1977	register char *p;
1978	register int c;
1979	FILE *fi;
1980	char pbuf[20];
1981
1982#ifndef SYS_WINNT
1983	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
1984#else
1985	    if ((fi = _fdopen((int)GetStdHandle(STD_INPUT_HANDLE), "r")) == NULL)
1986#endif /* SYS_WINNT */
1987		fi = stdin;
1988	    else
1989		setbuf(fi, (char *)NULL);
1990	fprintf(stderr, "%s", keyprompt); fflush(stderr);
1991	for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
1992		if (p < &pbuf[18])
1993		    *p++ = (char) c;
1994	}
1995	*p = '\0';
1996	if (fi != stdin)
1997	    fclose(fi);
1998	return (u_int32)atoi(pbuf);
1999}
2000