xref: /freebsd/contrib/ntp/ntpdc/ntpdc.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
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  */
39 static	int	interactive = 0;	/* set to 1 when we should prompt */
40 static	const char *	prompt = "ntpdc> ";	/* prompt to ask him about */
41 
42 /*
43  * Keyid used for authenticated requests.  Obtained on the fly.
44  */
45 static	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 
53 static	int info_auth_keytype = KEY_TYPE_MD5;	/* MD5*/
54 u_long	current_time;		/* needed by authkeys; not used */
55 
56 int		ntpdcmain	P((int,	char **));
57 /*
58  * Built in command handler declarations
59  */
60 static	int	openhost	P((const char *));
61 static	int	sendpkt		P((char *, int));
62 static	void	growpktdata	P((void));
63 static	int	getresponse	P((int, int, int *, int *, char **));
64 static	int	sendrequest	P((int, int, int, int, int, char *));
65 static	void	getcmds		P((void));
66 static	RETSIGTYPE abortcmd	P((int));
67 static	void	docmd		P((const char *));
68 static	void	tokenize	P((const char *, char **, int *));
69 static	int	findcmd		P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
70 static	int	getarg		P((char *, int, arg_v *));
71 static	int	getnetnum	P((const char *, u_int32 *, char *));
72 static	void	help		P((struct parse *, FILE *));
73 #ifdef QSORT_USES_VOID_P
74 static	int	helpsort	P((const void *, const void *));
75 #else
76 static	int	helpsort	P((char **, char **));
77 #endif
78 static	void	printusage	P((struct xcmd *, FILE *));
79 static	void	timeout		P((struct parse *, FILE *));
80 static	void	my_delay	P((struct parse *, FILE *));
81 static	void	host		P((struct parse *, FILE *));
82 static	void	keyid		P((struct parse *, FILE *));
83 static	void	keytype		P((struct parse *, FILE *));
84 static	void	passwd		P((struct parse *, FILE *));
85 static	void	hostnames	P((struct parse *, FILE *));
86 static	void	setdebug	P((struct parse *, FILE *));
87 static	void	quit		P((struct parse *, FILE *));
88 static	void	version		P((struct parse *, FILE *));
89 static	void	warning		P((const char *, const char *, const char *));
90 static	void	error		P((const char *, const char *, const char *));
91 static	u_long	getkeyid	P((const char *));
92 
93 
94 
95 /*
96  * Built-in commands we understand
97  */
98 static	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  */
159 static	struct timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
160 static	struct timeval tvsout = { DEFSTIMEOUT, 0 };	/* secondary time out */
161 static	l_fp delay_time;				/* delay time */
162 static	char currenthost[LENHOSTNAME];			/* current host name */
163 static	struct sockaddr_in hostaddr = { 0 };		/* host address */
164 static	int showhostnames = 1;				/* show host names by default */
165 
166 static	int sockfd;					/* fd socket is openned on */
167 static	int havehost = 0;				/* set to 1 when host open */
168 struct servent *server_entry = NULL;		/* server entry for ntp */
169 
170 #if defined (SYS_WINNT) || defined (SYS_VXWORKS)
171 char password[9];
172 #endif /* SYS_WINNT || SYS_VXWORKS */
173 
174 #ifdef SYS_WINNT
175 WORD wVersionRequested;
176 WSADATA wsaData;
177 DWORD NumberOfBytesWritten;
178 
179 HANDLE	TimerThreadHandle = NULL;	/* 1998/06/03 - Used in ntplib/machines.c */
180 void 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 
191 static	char *pktdata;
192 static	int pktdatasize;
193 
194 /*
195  * For commands typed on the command line (with the -c option)
196  */
197 static	int numcmds = 0;
198 static	const char *ccmds[MAXCMDS];
199 #define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
200 
201 /*
202  * When multiple hosts are specified.
203  */
204 static	int numhosts = 0;
205 static	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  */
229 static	jmp_buf interrupt_buf;
230 static  volatile int jump = 0;
231 
232 /*
233  * Pointer to current output unit
234  */
235 static	FILE *current_output;
236 
237 /*
238  * Command table imported from ntpdc_ops.c
239  */
240 extern struct xcmd opcmds[];
241 
242 char *progname;
243 volatile int debug;
244 
245 #ifdef NO_MAIN_ALLOWED
246 CALL(ntpdc,"ntpdc",ntpdcmain);
247 #else
248 int
249 main(
250 	int argc,
251 	char *argv[]
252 	)
253 {
254 	return ntpdcmain(argc, argv);
255 }
256 #endif
257 
258 #ifdef SYS_VXWORKS
259 void 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  */
276 int
277 ntpdcmain(
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  */
390 static int
391 openhost(
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  */
485 static int
486 sendpkt(
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  */
503 static void
504 growpktdata(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  */
518 static int
519 getresponse(
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  */
742 static int
743 sendrequest(
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  */
819 int
820 doquery(
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  */
918 static void
919 getcmds(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  */
954 static RETSIGTYPE
955 abortcmd(
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  */
971 static void
972 docmd(
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  */
1063 static void
1064 tokenize(
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  */
1095 static int
1096 findcmd(
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  */
1162 static int
1163 getarg(
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  */
1222 static int
1223 getnetnum(
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  */
1255 char *
1256 nntohost(
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  */
1275 static void
1276 help(
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
1346 static int
1347 helpsort(
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
1358 static int
1359 helpsort(
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  */
1372 static void
1373 printusage(
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  */
1394 static void
1395 timeout(
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  */
1416 static void
1417 my_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  */
1450 static void
1451 host(
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  */
1476 static void
1477 keyid(
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  */
1496 static void
1497 keytype(
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*/
1528 static void
1529 passwd(
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  */
1566 static void
1567 hostnames(
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  */
1591 static void
1592 setdebug(
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*/
1618 static void
1619 quit(
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*/
1634 static void
1635 version(
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  */
1649 static void
1650 warning(
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  */
1666 static void
1667 error(
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  */
1680 static u_long
1681 getkeyid(
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