Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / ntp / ntpdc / ntpdc.c
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 }