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