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