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