ntpq.c revision 1.10.2.2 1 /* $NetBSD: ntpq.c,v 1.10.2.2 2015/01/07 04:45:32 msaitoh Exp $ */
2
3 /*
4 * ntpq - query an NTP server using mode 6 commands
5 */
6 #include <config.h>
7 #include <stdio.h>
8 #include <ctype.h>
9 #include <signal.h>
10 #include <setjmp.h>
11 #include <sys/types.h>
12 #include <sys/time.h>
13 #ifdef HAVE_UNISTD_H
14 # include <unistd.h>
15 #endif
16 #ifdef HAVE_FCNTL_H
17 # include <fcntl.h>
18 #endif
19 #ifdef SYS_WINNT
20 # include <mswsock.h>
21 #endif
22 #include <isc/net.h>
23 #include <isc/result.h>
24
25 #include "ntpq.h"
26 #include "ntp_stdlib.h"
27 #include "ntp_unixtime.h"
28 #include "ntp_calendar.h"
29 #include "ntp_select.h"
30 #include "ntp_assert.h"
31 #include "lib_strbuf.h"
32 #include "ntp_lineedit.h"
33 #include "ntp_debug.h"
34 #ifdef OPENSSL
35 #include "openssl/evp.h"
36 #include "openssl/objects.h"
37 #endif
38 #include <ssl_applink.c>
39
40 #include "ntp_libopts.h"
41 #include "ntpq-opts.h"
42
43
44 #ifdef SYS_VXWORKS /* vxWorks needs mode flag -casey*/
45 # define open(name, flags) open(name, flags, 0777)
46 # define SERVER_PORT_NUM 123
47 #endif
48
49 /* we use COMMAND as an autogen keyword */
50 #ifdef COMMAND
51 # undef COMMAND
52 #endif
53
54 /*
55 * Because we potentially understand a lot of commands we will run
56 * interactive if connected to a terminal.
57 */
58 int interactive = 0; /* set to 1 when we should prompt */
59 const char *prompt = "ntpq> "; /* prompt to ask him about */
60
61 /*
62 * use old readvars behavior? --old-rv processing in ntpq resets
63 * this value based on the presence or absence of --old-rv. It is
64 * initialized to 1 here to maintain backward compatibility with
65 * libntpq clients such as ntpsnmpd, which are free to reset it as
66 * desired.
67 */
68 int old_rv = 1;
69
70
71 /*
72 * for get_systime()
73 */
74 s_char sys_precision; /* local clock precision (log2 s) */
75
76 /*
77 * Keyid used for authenticated requests. Obtained on the fly.
78 */
79 u_long info_auth_keyid = 0;
80
81 static int info_auth_keytype = NID_md5; /* MD5 */
82 static size_t info_auth_hashlen = 16; /* MD5 */
83 u_long current_time; /* needed by authkeys; not used */
84
85 /*
86 * Flag which indicates we should always send authenticated requests
87 */
88 int always_auth = 0;
89
90 /*
91 * Flag which indicates raw mode output.
92 */
93 int rawmode = 0;
94
95 /*
96 * Packet version number we use
97 */
98 u_char pktversion = NTP_OLDVERSION + 1;
99
100 /*
101 * Don't jump if no set jmp.
102 */
103 volatile int jump = 0;
104
105 /*
106 * Format values
107 */
108 #define PADDING 0
109 #define HA 1 /* host address */
110 #define NA 2 /* network address */
111 #define LP 3 /* leap (print in binary) */
112 #define RF 4 /* refid (sometimes string, sometimes not) */
113 #define AR 5 /* array of times */
114 #define FX 6 /* test flags */
115 #define TS 7 /* l_fp timestamp in hex */
116 #define OC 8 /* integer, print in octal */
117 #define EOV 255 /* end of table */
118
119 /*
120 * For the most part ntpq simply displays what ntpd provides in the
121 * mostly plain-text mode 6 responses. A few variable names are by
122 * default "cooked" to provide more human-friendly output.
123 */
124 const var_format cookedvars[] = {
125 { "leap", LP },
126 { "reach", OC },
127 { "refid", RF },
128 { "reftime", TS },
129 { "clock", TS },
130 { "org", TS },
131 { "rec", TS },
132 { "xmt", TS },
133 { "flash", FX },
134 { "srcadr", HA },
135 { "peeradr", HA }, /* compat with others */
136 { "dstadr", NA },
137 { "filtdelay", AR },
138 { "filtoffset", AR },
139 { "filtdisp", AR },
140 { "filterror", AR }, /* compat with others */
141 };
142
143
144
145 /*
146 * flasher bits
147 */
148 static const char *tstflagnames[] = {
149 "pkt_dup", /* TEST1 */
150 "pkt_bogus", /* TEST2 */
151 "pkt_unsync", /* TEST3 */
152 "pkt_denied", /* TEST4 */
153 "pkt_auth", /* TEST5 */
154 "pkt_stratum", /* TEST6 */
155 "pkt_header", /* TEST7 */
156 "pkt_autokey", /* TEST8 */
157 "pkt_crypto", /* TEST9 */
158 "peer_stratum", /* TEST10 */
159 "peer_dist", /* TEST11 */
160 "peer_loop", /* TEST12 */
161 "peer_unreach" /* TEST13 */
162 };
163
164
165 int ntpqmain (int, char **);
166 /*
167 * Built in command handler declarations
168 */
169 static int openhost (const char *, int);
170 static void dump_hex_printable(const void *, size_t);
171 static int sendpkt (void *, size_t);
172 static int getresponse (int, int, u_short *, int *, const char **, int);
173 static int sendrequest (int, associd_t, int, int, const char *);
174 static char * tstflags (u_long);
175 #ifndef BUILD_AS_LIB
176 static void getcmds (void);
177 #ifndef SYS_WINNT
178 static RETSIGTYPE abortcmd (int);
179 #endif /* SYS_WINNT */
180 static void docmd (const char *);
181 static void tokenize (const char *, char **, int *);
182 static int getarg (const char *, int, arg_v *);
183 #endif /* BUILD_AS_LIB */
184 static int findcmd (const char *, struct xcmd *,
185 struct xcmd *, struct xcmd **);
186 static int rtdatetolfp (char *, l_fp *);
187 static int decodearr (char *, int *, l_fp *);
188 static void help (struct parse *, FILE *);
189 static int helpsort (const void *, const void *);
190 static void printusage (struct xcmd *, FILE *);
191 static void timeout (struct parse *, FILE *);
192 static void auth_delay (struct parse *, FILE *);
193 static void host (struct parse *, FILE *);
194 static void ntp_poll (struct parse *, FILE *);
195 static void keyid (struct parse *, FILE *);
196 static void keytype (struct parse *, FILE *);
197 static void passwd (struct parse *, FILE *);
198 static void hostnames (struct parse *, FILE *);
199 static void setdebug (struct parse *, FILE *);
200 static void quit (struct parse *, FILE *);
201 static void version (struct parse *, FILE *);
202 static void raw (struct parse *, FILE *);
203 static void cooked (struct parse *, FILE *);
204 static void authenticate (struct parse *, FILE *);
205 static void ntpversion (struct parse *, FILE *);
206 static void warning (const char *, ...)
207 __attribute__((__format__(__printf__, 1, 2)));
208 static void error (const char *, ...)
209 __attribute__((__format__(__printf__, 1, 2)));
210 static u_long getkeyid (const char *);
211 static void atoascii (const char *, size_t, char *, size_t);
212 static void cookedprint (int, int, const char *, int, int, FILE *);
213 static void rawprint (int, int, const char *, int, int, FILE *);
214 static void startoutput (void);
215 static void output (FILE *, const char *, const char *);
216 static void endoutput (FILE *);
217 static void outputarr (FILE *, char *, int, l_fp *);
218 static int assoccmp (const void *, const void *);
219 u_short varfmt (const char *);
220
221 void ntpq_custom_opt_handler (tOptions *, tOptDesc *);
222
223
224 /*
225 * Built-in commands we understand
226 */
227 struct xcmd builtins[] = {
228 { "?", help, { OPT|NTP_STR, NO, NO, NO },
229 { "command", "", "", "" },
230 "tell the use and syntax of commands" },
231 { "help", help, { OPT|NTP_STR, NO, NO, NO },
232 { "command", "", "", "" },
233 "tell the use and syntax of commands" },
234 { "timeout", timeout, { OPT|NTP_UINT, NO, NO, NO },
235 { "msec", "", "", "" },
236 "set the primary receive time out" },
237 { "delay", auth_delay, { OPT|NTP_INT, NO, NO, NO },
238 { "msec", "", "", "" },
239 "set the delay added to encryption time stamps" },
240 { "host", host, { OPT|NTP_STR, OPT|NTP_STR, NO, NO },
241 { "-4|-6", "hostname", "", "" },
242 "specify the host whose NTP server we talk to" },
243 { "poll", ntp_poll, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
244 { "n", "verbose", "", "" },
245 "poll an NTP server in client mode `n' times" },
246 { "passwd", passwd, { OPT|NTP_STR, NO, NO, NO },
247 { "", "", "", "" },
248 "specify a password to use for authenticated requests"},
249 { "hostnames", hostnames, { OPT|NTP_STR, NO, NO, NO },
250 { "yes|no", "", "", "" },
251 "specify whether hostnames or net numbers are printed"},
252 { "debug", setdebug, { OPT|NTP_STR, NO, NO, NO },
253 { "no|more|less", "", "", "" },
254 "set/change debugging level" },
255 { "quit", quit, { NO, NO, NO, NO },
256 { "", "", "", "" },
257 "exit ntpq" },
258 { "exit", quit, { NO, NO, NO, NO },
259 { "", "", "", "" },
260 "exit ntpq" },
261 { "keyid", keyid, { OPT|NTP_UINT, NO, NO, NO },
262 { "key#", "", "", "" },
263 "set keyid to use for authenticated requests" },
264 { "version", version, { NO, NO, NO, NO },
265 { "", "", "", "" },
266 "print version number" },
267 { "raw", raw, { NO, NO, NO, NO },
268 { "", "", "", "" },
269 "do raw mode variable output" },
270 { "cooked", cooked, { NO, NO, NO, NO },
271 { "", "", "", "" },
272 "do cooked mode variable output" },
273 { "authenticate", authenticate, { OPT|NTP_STR, NO, NO, NO },
274 { "yes|no", "", "", "" },
275 "always authenticate requests to this server" },
276 { "ntpversion", ntpversion, { OPT|NTP_UINT, NO, NO, NO },
277 { "version number", "", "", "" },
278 "set the NTP version number to use for requests" },
279 { "keytype", keytype, { OPT|NTP_STR, NO, NO, NO },
280 { "key type (md5|des)", "", "", "" },
281 "set key type to use for authenticated requests (des|md5)" },
282 { 0, 0, { NO, NO, NO, NO },
283 { "", "", "", "" }, "" }
284 };
285
286
287 /*
288 * Default values we use.
289 */
290 #define DEFHOST "localhost" /* default host name */
291 #define DEFTIMEOUT 5 /* wait 5 seconds for 1st pkt */
292 #define DEFSTIMEOUT 3 /* and 3 more for each additional */
293 /*
294 * Requests are automatically retried once, so total timeout with no
295 * response is a bit over 2 * DEFTIMEOUT, or 10 seconds. At the other
296 * extreme, a request eliciting 32 packets of responses each for some
297 * reason nearly DEFSTIMEOUT seconds after the prior in that series,
298 * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
299 * 93 seconds to fail each of two times, or 186 seconds.
300 * Some commands involve a series of requests, such as "peers" and
301 * "mrulist", so the cumulative timeouts are even longer for those.
302 */
303 #define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
304 #define LENHOSTNAME 256 /* host name is 256 characters long */
305 #define MAXCMDS 100 /* maximum commands on cmd line */
306 #define MAXHOSTS 200 /* maximum hosts on cmd line */
307 #define MAXLINE 512 /* maximum line length */
308 #define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */
309 #define MAXVARLEN 256 /* maximum length of a variable name */
310 #define MAXVALLEN 2048 /* maximum length of a variable value */
311 #define MAXOUTLINE 72 /* maximum length of an output line */
312 #define SCREENWIDTH 76 /* nominal screen width in columns */
313
314 /*
315 * Some variables used and manipulated locally
316 */
317 struct sock_timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */
318 struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
319 l_fp delay_time; /* delay time */
320 char currenthost[LENHOSTNAME]; /* current host name */
321 int currenthostisnum; /* is prior text from IP? */
322 struct sockaddr_in hostaddr; /* host address */
323 int showhostnames = 1; /* show host names by default */
324 int wideremote = 0; /* show wide remote names? */
325
326 int ai_fam_templ; /* address family */
327 int ai_fam_default; /* default address family */
328 SOCKET sockfd; /* fd socket is opened on */
329 int havehost = 0; /* set to 1 when host open */
330 int s_port = 0;
331 struct servent *server_entry = NULL; /* server entry for ntp */
332
333
334 /*
335 * Sequence number used for requests. It is incremented before
336 * it is used.
337 */
338 u_short sequence;
339
340 /*
341 * Holds data returned from queries. Declare buffer long to be sure of
342 * alignment.
343 */
344 #define DATASIZE (MAXFRAGS*480) /* maximum amount of data */
345 long pktdata[DATASIZE/sizeof(long)];
346
347 /*
348 * assoc_cache[] is a dynamic array which allows references to
349 * associations using &1 ... &N for n associations, avoiding manual
350 * lookup of the current association IDs for a given ntpd. It also
351 * caches the status word for each association, retrieved incidentally.
352 */
353 struct association * assoc_cache;
354 u_int assoc_cache_slots;/* count of allocated array entries */
355 u_int numassoc; /* number of cached associations */
356
357 /*
358 * For commands typed on the command line (with the -c option)
359 */
360 int numcmds = 0;
361 const char *ccmds[MAXCMDS];
362 #define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
363
364 /*
365 * When multiple hosts are specified.
366 */
367
368 u_int numhosts;
369
370 chost chosts[MAXHOSTS];
371 #define ADDHOST(cp) \
372 do { \
373 if (numhosts < MAXHOSTS) { \
374 chosts[numhosts].name = (cp); \
375 chosts[numhosts].fam = ai_fam_templ; \
376 numhosts++; \
377 } \
378 } while (0)
379
380 /*
381 * Macro definitions we use
382 */
383 #define ISSPACE(c) ((c) == ' ' || (c) == '\t')
384 #define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0')
385 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
386
387 /*
388 * Jump buffer for longjumping back to the command level
389 */
390 jmp_buf interrupt_buf;
391
392 /*
393 * Points at file being currently printed into
394 */
395 FILE *current_output;
396
397 /*
398 * Command table imported from ntpdc_ops.c
399 */
400 extern struct xcmd opcmds[];
401
402 char *progname;
403
404 #ifdef NO_MAIN_ALLOWED
405 #ifndef BUILD_AS_LIB
406 CALL(ntpq,"ntpq",ntpqmain);
407
408 void clear_globals(void)
409 {
410 extern int ntp_optind;
411 showhostnames = 0; /* don'tshow host names by default */
412 ntp_optind = 0;
413 server_entry = NULL; /* server entry for ntp */
414 havehost = 0; /* set to 1 when host open */
415 numassoc = 0; /* number of cached associations */
416 numcmds = 0;
417 numhosts = 0;
418 }
419 #endif /* !BUILD_AS_LIB */
420 #endif /* NO_MAIN_ALLOWED */
421
422 /*
423 * main - parse arguments and handle options
424 */
425 #ifndef NO_MAIN_ALLOWED
426 int
427 main(
428 int argc,
429 char *argv[]
430 )
431 {
432 return ntpqmain(argc, argv);
433 }
434 #endif
435
436 #ifndef BUILD_AS_LIB
437 int
438 ntpqmain(
439 int argc,
440 char *argv[]
441 )
442 {
443 u_int ihost;
444 int icmd;
445
446
447 #ifdef SYS_VXWORKS
448 clear_globals();
449 taskPrioritySet(taskIdSelf(), 100 );
450 #endif
451
452 delay_time.l_ui = 0;
453 delay_time.l_uf = DEFDELAY;
454
455 init_lib(); /* sets up ipv4_works, ipv6_works */
456 ssl_applink();
457 init_auth();
458
459 /* Check to see if we have IPv6. Otherwise default to IPv4 */
460 if (!ipv6_works)
461 ai_fam_default = AF_INET;
462
463 progname = argv[0];
464
465 {
466 int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
467 argc -= optct;
468 argv += optct;
469 }
470
471 /*
472 * Process options other than -c and -p, which are specially
473 * handled by ntpq_custom_opt_handler().
474 */
475
476 debug = OPT_VALUE_SET_DEBUG_LEVEL;
477
478 if (HAVE_OPT(IPV4))
479 ai_fam_templ = AF_INET;
480 else if (HAVE_OPT(IPV6))
481 ai_fam_templ = AF_INET6;
482 else
483 ai_fam_templ = ai_fam_default;
484
485 if (HAVE_OPT(INTERACTIVE))
486 interactive = 1;
487
488 if (HAVE_OPT(NUMERIC))
489 showhostnames = 0;
490
491 if (HAVE_OPT(WIDE))
492 wideremote = 1;
493
494 old_rv = HAVE_OPT(OLD_RV);
495
496 if (0 == argc) {
497 ADDHOST(DEFHOST);
498 } else {
499 for (ihost = 0; ihost < (u_int)argc; ihost++) {
500 if ('-' == *argv[ihost]) {
501 //
502 // If I really cared I'd also check:
503 // 0 == argv[ihost][2]
504 //
505 // and there are other cases as well...
506 //
507 if ('4' == argv[ihost][1]) {
508 ai_fam_templ = AF_INET;
509 continue;
510 } else if ('6' == argv[ihost][1]) {
511 ai_fam_templ = AF_INET6;
512 continue;
513 } else {
514 // XXX Throw a usage error
515 }
516 }
517 ADDHOST(argv[ihost]);
518 }
519 }
520
521 if (numcmds == 0 && interactive == 0
522 && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
523 interactive = 1;
524 }
525
526 #ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
527 if (interactive)
528 (void) signal_no_reset(SIGINT, abortcmd);
529 #endif /* SYS_WINNT */
530
531 if (numcmds == 0) {
532 (void) openhost(chosts[0].name, chosts[0].fam);
533 getcmds();
534 } else {
535 for (ihost = 0; ihost < numhosts; ihost++) {
536 if (openhost(chosts[ihost].name, chosts[ihost].fam))
537 for (icmd = 0; icmd < numcmds; icmd++)
538 docmd(ccmds[icmd]);
539 }
540 }
541 #ifdef SYS_WINNT
542 WSACleanup();
543 #endif /* SYS_WINNT */
544 return 0;
545 }
546 #endif /* !BUILD_AS_LIB */
547
548 /*
549 * openhost - open a socket to a host
550 */
551 static int
552 openhost(
553 const char *hname,
554 int fam
555 )
556 {
557 const char svc[] = "ntp";
558 char temphost[LENHOSTNAME];
559 int a_info, i;
560 struct addrinfo hints, *ai;
561 sockaddr_u addr;
562 size_t octets;
563 register const char *cp;
564 char name[LENHOSTNAME];
565
566 /*
567 * We need to get by the [] if they were entered
568 */
569
570 cp = hname;
571
572 if (*cp == '[') {
573 cp++;
574 for (i = 0; *cp && *cp != ']'; cp++, i++)
575 name[i] = *cp;
576 if (*cp == ']') {
577 name[i] = '\0';
578 hname = name;
579 } else {
580 return 0;
581 }
582 }
583
584 /*
585 * First try to resolve it as an ip address and if that fails,
586 * do a fullblown (dns) lookup. That way we only use the dns
587 * when it is needed and work around some implementations that
588 * will return an "IPv4-mapped IPv6 address" address if you
589 * give it an IPv4 address to lookup.
590 */
591 ZERO(hints);
592 hints.ai_family = fam;
593 hints.ai_protocol = IPPROTO_UDP;
594 hints.ai_socktype = SOCK_DGRAM;
595 hints.ai_flags = Z_AI_NUMERICHOST;
596 ai = NULL;
597
598 a_info = getaddrinfo(hname, svc, &hints, &ai);
599 if (a_info == EAI_NONAME
600 #ifdef EAI_NODATA
601 || a_info == EAI_NODATA
602 #endif
603 ) {
604 hints.ai_flags = AI_CANONNAME;
605 #ifdef AI_ADDRCONFIG
606 hints.ai_flags |= AI_ADDRCONFIG;
607 #endif
608 a_info = getaddrinfo(hname, svc, &hints, &ai);
609 }
610 #ifdef AI_ADDRCONFIG
611 /* Some older implementations don't like AI_ADDRCONFIG. */
612 if (a_info == EAI_BADFLAGS) {
613 hints.ai_flags &= ~AI_ADDRCONFIG;
614 a_info = getaddrinfo(hname, svc, &hints, &ai);
615 }
616 #endif
617 if (a_info != 0) {
618 fprintf(stderr, "%s\n", gai_strerror(a_info));
619 return 0;
620 }
621
622 INSIST(ai != NULL);
623 ZERO(addr);
624 octets = min(sizeof(addr), ai->ai_addrlen);
625 memcpy(&addr, ai->ai_addr, octets);
626
627 if (ai->ai_canonname == NULL) {
628 strlcpy(temphost, stoa(&addr), sizeof(temphost));
629 currenthostisnum = TRUE;
630 } else {
631 strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
632 currenthostisnum = FALSE;
633 }
634
635 if (debug > 2)
636 printf("Opening host %s (%s)\n",
637 temphost,
638 (ai->ai_family == AF_INET)
639 ? "AF_INET"
640 : (ai->ai_family == AF_INET6)
641 ? "AF_INET6"
642 : "AF-???"
643 );
644
645 if (havehost == 1) {
646 if (debug > 2)
647 printf("Closing old host %s\n", currenthost);
648 closesocket(sockfd);
649 havehost = 0;
650 }
651 strlcpy(currenthost, temphost, sizeof(currenthost));
652
653 /* port maps to the same location in both families */
654 s_port = NSRCPORT(&addr);
655 #ifdef SYS_VXWORKS
656 ((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
657 if (ai->ai_family == AF_INET)
658 *(struct sockaddr_in *)&hostaddr=
659 *((struct sockaddr_in *)ai->ai_addr);
660 else
661 *(struct sockaddr_in6 *)&hostaddr=
662 *((struct sockaddr_in6 *)ai->ai_addr);
663 #endif /* SYS_VXWORKS */
664
665 #ifdef SYS_WINNT
666 {
667 int optionValue = SO_SYNCHRONOUS_NONALERT;
668 int err;
669
670 err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
671 (char *)&optionValue, sizeof(optionValue));
672 if (err) {
673 mfprintf(stderr,
674 "setsockopt(SO_SYNCHRONOUS_NONALERT)"
675 " error: %m\n");
676 freeaddrinfo(ai);
677 exit(1);
678 }
679 }
680 #endif /* SYS_WINNT */
681
682 sockfd = socket(ai->ai_family, ai->ai_socktype,
683 ai->ai_protocol);
684 if (sockfd == INVALID_SOCKET) {
685 error("socket");
686 freeaddrinfo(ai);
687 return 0;
688 }
689
690
691 #ifdef NEED_RCVBUF_SLOP
692 # ifdef SO_RCVBUF
693 { int rbufsize = DATASIZE + 2048; /* 2K for slop */
694 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
695 &rbufsize, sizeof(int)) == -1)
696 error("setsockopt");
697 }
698 # endif
699 #endif
700
701 if
702 #ifdef SYS_VXWORKS
703 (connect(sockfd, (struct sockaddr *)&hostaddr,
704 sizeof(hostaddr)) == -1)
705 #else
706 (connect(sockfd, (struct sockaddr *)ai->ai_addr,
707 ai->ai_addrlen) == -1)
708 #endif /* SYS_VXWORKS */
709 {
710 error("connect");
711 freeaddrinfo(ai);
712 return 0;
713 }
714 freeaddrinfo(ai);
715 havehost = 1;
716 numassoc = 0;
717
718 return 1;
719 }
720
721
722 static void
723 dump_hex_printable(
724 const void * data,
725 size_t len
726 )
727 {
728 const char * cdata;
729 const char * rowstart;
730 size_t idx;
731 size_t rowlen;
732 u_char uch;
733
734 cdata = data;
735 while (len > 0) {
736 rowstart = cdata;
737 rowlen = min(16, len);
738 for (idx = 0; idx < rowlen; idx++) {
739 uch = *(cdata++);
740 printf("%02x ", uch);
741 }
742 for ( ; idx < 16 ; idx++)
743 printf(" ");
744 cdata = rowstart;
745 for (idx = 0; idx < rowlen; idx++) {
746 uch = *(cdata++);
747 printf("%c", (isprint(uch))
748 ? uch
749 : '.');
750 }
751 printf("\n");
752 len -= rowlen;
753 }
754 }
755
756
757 /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
758 /*
759 * sendpkt - send a packet to the remote host
760 */
761 static int
762 sendpkt(
763 void * xdata,
764 size_t xdatalen
765 )
766 {
767 if (debug >= 3)
768 printf("Sending %zu octets\n", xdatalen);
769
770 if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) {
771 warning("write to %s failed", currenthost);
772 return -1;
773 }
774
775 if (debug >= 4) {
776 printf("Request packet:\n");
777 dump_hex_printable(xdata, xdatalen);
778 }
779 return 0;
780 }
781
782 /*
783 * getresponse - get a (series of) response packet(s) and return the data
784 */
785 static int
786 getresponse(
787 int opcode,
788 int associd,
789 u_short *rstatus,
790 int *rsize,
791 const char **rdata,
792 int timeo
793 )
794 {
795 struct ntp_control rpkt;
796 struct sock_timeval tvo;
797 u_short offsets[MAXFRAGS+1];
798 u_short counts[MAXFRAGS+1];
799 u_short offset;
800 u_short count;
801 size_t numfrags;
802 size_t f;
803 size_t ff;
804 int seenlastfrag;
805 int shouldbesize;
806 fd_set fds;
807 int n;
808 int errcode;
809
810 /*
811 * This is pretty tricky. We may get between 1 and MAXFRAG packets
812 * back in response to the request. We peel the data out of
813 * each packet and collect it in one long block. When the last
814 * packet in the sequence is received we'll know how much data we
815 * should have had. Note we use one long time out, should reconsider.
816 */
817 *rsize = 0;
818 if (rstatus)
819 *rstatus = 0;
820 *rdata = (char *)pktdata;
821
822 numfrags = 0;
823 seenlastfrag = 0;
824
825 FD_ZERO(&fds);
826
827 /*
828 * Loop until we have an error or a complete response. Nearly all
829 * code paths to loop again use continue.
830 */
831 for (;;) {
832
833 if (numfrags == 0)
834 tvo = tvout;
835 else
836 tvo = tvsout;
837
838 FD_SET(sockfd, &fds);
839 n = select(sockfd + 1, &fds, NULL, NULL, &tvo);
840
841 if (n == -1) {
842 warning("select fails");
843 return -1;
844 }
845 if (n == 0) {
846 /*
847 * Timed out. Return what we have
848 */
849 if (numfrags == 0) {
850 if (timeo)
851 fprintf(stderr,
852 "%s: timed out, nothing received\n",
853 currenthost);
854 return ERR_TIMEOUT;
855 }
856 if (timeo)
857 fprintf(stderr,
858 "%s: timed out with incomplete data\n",
859 currenthost);
860 if (debug) {
861 fprintf(stderr,
862 "ERR_INCOMPLETE: Received fragments:\n");
863 for (f = 0; f < numfrags; f++)
864 fprintf(stderr,
865 "%2u: %5d %5d\t%3d octets\n",
866 (u_int)f, offsets[f],
867 offsets[f] +
868 counts[f],
869 counts[f]);
870 fprintf(stderr,
871 "last fragment %sreceived\n",
872 (seenlastfrag)
873 ? ""
874 : "not ");
875 }
876 return ERR_INCOMPLETE;
877 }
878
879 n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
880 if (n == -1) {
881 warning("read");
882 return -1;
883 }
884
885 if (debug >= 4) {
886 printf("Response packet:\n");
887 dump_hex_printable(&rpkt, n);
888 }
889
890 /*
891 * Check for format errors. Bug proofing.
892 */
893 if (n < (int)CTL_HEADER_LEN) {
894 if (debug)
895 printf("Short (%d byte) packet received\n", n);
896 continue;
897 }
898 if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
899 || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
900 if (debug)
901 printf("Packet received with version %d\n",
902 PKT_VERSION(rpkt.li_vn_mode));
903 continue;
904 }
905 if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
906 if (debug)
907 printf("Packet received with mode %d\n",
908 PKT_MODE(rpkt.li_vn_mode));
909 continue;
910 }
911 if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
912 if (debug)
913 printf("Received request packet, wanted response\n");
914 continue;
915 }
916
917 /*
918 * Check opcode and sequence number for a match.
919 * Could be old data getting to us.
920 */
921 if (ntohs(rpkt.sequence) != sequence) {
922 if (debug)
923 printf("Received sequnce number %d, wanted %d\n",
924 ntohs(rpkt.sequence), sequence);
925 continue;
926 }
927 if (CTL_OP(rpkt.r_m_e_op) != opcode) {
928 if (debug)
929 printf(
930 "Received opcode %d, wanted %d (sequence number okay)\n",
931 CTL_OP(rpkt.r_m_e_op), opcode);
932 continue;
933 }
934
935 /*
936 * Check the error code. If non-zero, return it.
937 */
938 if (CTL_ISERROR(rpkt.r_m_e_op)) {
939 errcode = (ntohs(rpkt.status) >> 8) & 0xff;
940 if (CTL_ISMORE(rpkt.r_m_e_op))
941 TRACE(1, ("Error code %d received on not-final packet\n",
942 errcode));
943 if (errcode == CERR_UNSPEC)
944 return ERR_UNSPEC;
945 return errcode;
946 }
947
948 /*
949 * Check the association ID to make sure it matches what
950 * we sent.
951 */
952 if (ntohs(rpkt.associd) != associd) {
953 TRACE(1, ("Association ID %d doesn't match expected %d\n",
954 ntohs(rpkt.associd), associd));
955 /*
956 * Hack for silly fuzzballs which, at the time of writing,
957 * return an assID of sys.peer when queried for system variables.
958 */
959 #ifdef notdef
960 continue;
961 #endif
962 }
963
964 /*
965 * Collect offset and count. Make sure they make sense.
966 */
967 offset = ntohs(rpkt.offset);
968 count = ntohs(rpkt.count);
969
970 /*
971 * validate received payload size is padded to next 32-bit
972 * boundary and no smaller than claimed by rpkt.count
973 */
974 if (n & 0x3) {
975 TRACE(1, ("Response packet not padded, size = %d\n",
976 n));
977 continue;
978 }
979
980 shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
981
982 if (n < shouldbesize) {
983 printf("Response packet claims %u octets payload, above %ld received\n",
984 count, (long)n - CTL_HEADER_LEN);
985 return ERR_INCOMPLETE;
986 }
987
988 if (debug >= 3 && shouldbesize > n) {
989 u_int32 key;
990 u_int32 *lpkt;
991 int maclen;
992
993 /*
994 * Usually we ignore authentication, but for debugging purposes
995 * we watch it here.
996 */
997 /* round to 8 octet boundary */
998 shouldbesize = (shouldbesize + 7) & ~7;
999
1000 maclen = n - shouldbesize;
1001 if (maclen >= (int)MIN_MAC_LEN) {
1002 printf(
1003 "Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1004 n, shouldbesize, maclen);
1005 lpkt = (u_int32 *)&rpkt;
1006 printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1007 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1008 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1009 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1010 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1011 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1012 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1013 key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1014 printf("Authenticated with keyid %lu\n", (u_long)key);
1015 if (key != 0 && key != info_auth_keyid) {
1016 printf("We don't know that key\n");
1017 } else {
1018 if (authdecrypt(key, (u_int32 *)&rpkt,
1019 n - maclen, maclen)) {
1020 printf("Auth okay!\n");
1021 } else {
1022 printf("Auth failed!\n");
1023 }
1024 }
1025 }
1026 }
1027
1028 TRACE(2, ("Got packet, size = %d\n", n));
1029 if (count > (n - CTL_HEADER_LEN)) {
1030 TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
1031 count, (long)n - CTL_HEADER_LEN));
1032 continue;
1033 }
1034 if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1035 TRACE(1, ("Received count of 0 in non-final fragment\n"));
1036 continue;
1037 }
1038 if (offset + count > sizeof(pktdata)) {
1039 TRACE(1, ("Offset %u, count %u, too big for buffer\n",
1040 offset, count));
1041 return ERR_TOOMUCH;
1042 }
1043 if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1044 TRACE(1, ("Received second last fragment packet\n"));
1045 continue;
1046 }
1047
1048 /*
1049 * So far, so good. Record this fragment, making sure it doesn't
1050 * overlap anything.
1051 */
1052 TRACE(2, ("Packet okay\n"));
1053
1054 if (numfrags > (MAXFRAGS - 1)) {
1055 TRACE(2, ("Number of fragments exceeds maximum %d\n",
1056 MAXFRAGS - 1));
1057 return ERR_TOOMUCH;
1058 }
1059
1060 /*
1061 * Find the position for the fragment relative to any
1062 * previously received.
1063 */
1064 for (f = 0;
1065 f < numfrags && offsets[f] < offset;
1066 f++) {
1067 /* empty body */ ;
1068 }
1069
1070 if (f < numfrags && offset == offsets[f]) {
1071 TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
1072 count, offset, counts[f], offsets[f]));
1073 continue;
1074 }
1075
1076 if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
1077 TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
1078 offset, counts[f-1], offsets[f-1]));
1079 continue;
1080 }
1081
1082 if (f < numfrags && (offset + count) > offsets[f]) {
1083 TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
1084 count, offset, offsets[f]));
1085 continue;
1086 }
1087
1088 for (ff = numfrags; ff > f; ff--) {
1089 offsets[ff] = offsets[ff-1];
1090 counts[ff] = counts[ff-1];
1091 }
1092 offsets[f] = offset;
1093 counts[f] = count;
1094 numfrags++;
1095
1096 /*
1097 * Got that stuffed in right. Figure out if this was the last.
1098 * Record status info out of the last packet.
1099 */
1100 if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1101 seenlastfrag = 1;
1102 if (rstatus != 0)
1103 *rstatus = ntohs(rpkt.status);
1104 }
1105
1106 /*
1107 * Copy the data into the data buffer.
1108 */
1109 memcpy((char *)pktdata + offset, &rpkt.u, count);
1110
1111 /*
1112 * If we've seen the last fragment, look for holes in the sequence.
1113 * If there aren't any, we're done.
1114 */
1115 if (seenlastfrag && offsets[0] == 0) {
1116 for (f = 1; f < numfrags; f++)
1117 if (offsets[f-1] + counts[f-1] !=
1118 offsets[f])
1119 break;
1120 if (f == numfrags) {
1121 *rsize = offsets[f-1] + counts[f-1];
1122 TRACE(1, ("%lu packets reassembled into response\n",
1123 (u_long)numfrags));
1124 return 0;
1125 }
1126 }
1127 } /* giant for (;;) collecting response packets */
1128 } /* getresponse() */
1129
1130
1131 /*
1132 * sendrequest - format and send a request packet
1133 */
1134 static int
1135 sendrequest(
1136 int opcode,
1137 associd_t associd,
1138 int auth,
1139 int qsize,
1140 const char *qdata
1141 )
1142 {
1143 struct ntp_control qpkt;
1144 int pktsize;
1145 u_long key_id;
1146 char * pass;
1147 int maclen;
1148
1149 /*
1150 * Check to make sure the data will fit in one packet
1151 */
1152 if (qsize > CTL_MAX_DATA_LEN) {
1153 fprintf(stderr,
1154 "***Internal error! qsize (%d) too large\n",
1155 qsize);
1156 return 1;
1157 }
1158
1159 /*
1160 * Fill in the packet
1161 */
1162 qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1163 qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
1164 qpkt.sequence = htons(sequence);
1165 qpkt.status = 0;
1166 qpkt.associd = htons((u_short)associd);
1167 qpkt.offset = 0;
1168 qpkt.count = htons((u_short)qsize);
1169
1170 pktsize = CTL_HEADER_LEN;
1171
1172 /*
1173 * If we have data, copy and pad it out to a 32-bit boundary.
1174 */
1175 if (qsize > 0) {
1176 memcpy(&qpkt.u, qdata, (size_t)qsize);
1177 pktsize += qsize;
1178 while (pktsize & (sizeof(u_int32) - 1)) {
1179 qpkt.u.data[qsize++] = 0;
1180 pktsize++;
1181 }
1182 }
1183
1184 /*
1185 * If it isn't authenticated we can just send it. Otherwise
1186 * we're going to have to think about it a little.
1187 */
1188 if (!auth && !always_auth) {
1189 return sendpkt(&qpkt, pktsize);
1190 }
1191
1192 /*
1193 * Pad out packet to a multiple of 8 octets to be sure
1194 * receiver can handle it.
1195 */
1196 while (pktsize & 7) {
1197 qpkt.u.data[qsize++] = 0;
1198 pktsize++;
1199 }
1200
1201 /*
1202 * Get the keyid and the password if we don't have one.
1203 */
1204 if (info_auth_keyid == 0) {
1205 key_id = getkeyid("Keyid: ");
1206 if (key_id == 0 || key_id > NTP_MAXKEY) {
1207 fprintf(stderr,
1208 "Invalid key identifier\n");
1209 return 1;
1210 }
1211 info_auth_keyid = key_id;
1212 }
1213 if (!authistrusted(info_auth_keyid)) {
1214 pass = getpass_keytype(info_auth_keytype);
1215 if ('\0' == pass[0]) {
1216 fprintf(stderr, "Invalid password\n");
1217 return 1;
1218 }
1219 authusekey(info_auth_keyid, info_auth_keytype,
1220 (u_char *)pass);
1221 authtrust(info_auth_keyid, 1);
1222 }
1223
1224 /*
1225 * Do the encryption.
1226 */
1227 maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1228 if (!maclen) {
1229 fprintf(stderr, "Key not found\n");
1230 return 1;
1231 } else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1232 fprintf(stderr,
1233 "%d octet MAC, %zu expected with %zu octet digest\n",
1234 maclen, (info_auth_hashlen + sizeof(keyid_t)),
1235 info_auth_hashlen);
1236 return 1;
1237 }
1238
1239 return sendpkt((char *)&qpkt, pktsize + maclen);
1240 }
1241
1242
1243 /*
1244 * show_error_msg - display the error text for a mode 6 error response.
1245 */
1246 void
1247 show_error_msg(
1248 int m6resp,
1249 associd_t associd
1250 )
1251 {
1252 if (numhosts > 1)
1253 fprintf(stderr, "server=%s ", currenthost);
1254
1255 switch(m6resp) {
1256
1257 case CERR_BADFMT:
1258 fprintf(stderr,
1259 "***Server reports a bad format request packet\n");
1260 break;
1261
1262 case CERR_PERMISSION:
1263 fprintf(stderr,
1264 "***Server disallowed request (authentication?)\n");
1265 break;
1266
1267 case CERR_BADOP:
1268 fprintf(stderr,
1269 "***Server reports a bad opcode in request\n");
1270 break;
1271
1272 case CERR_BADASSOC:
1273 fprintf(stderr,
1274 "***Association ID %d unknown to server\n",
1275 associd);
1276 break;
1277
1278 case CERR_UNKNOWNVAR:
1279 fprintf(stderr,
1280 "***A request variable unknown to the server\n");
1281 break;
1282
1283 case CERR_BADVALUE:
1284 fprintf(stderr,
1285 "***Server indicates a request variable was bad\n");
1286 break;
1287
1288 case ERR_UNSPEC:
1289 fprintf(stderr,
1290 "***Server returned an unspecified error\n");
1291 break;
1292
1293 case ERR_TIMEOUT:
1294 fprintf(stderr, "***Request timed out\n");
1295 break;
1296
1297 case ERR_INCOMPLETE:
1298 fprintf(stderr,
1299 "***Response from server was incomplete\n");
1300 break;
1301
1302 case ERR_TOOMUCH:
1303 fprintf(stderr,
1304 "***Buffer size exceeded for returned data\n");
1305 break;
1306
1307 default:
1308 fprintf(stderr,
1309 "***Server returns unknown error code %d\n",
1310 m6resp);
1311 }
1312 }
1313
1314 /*
1315 * doquery - send a request and process the response, displaying
1316 * error messages for any error responses.
1317 */
1318 int
1319 doquery(
1320 int opcode,
1321 associd_t associd,
1322 int auth,
1323 int qsize,
1324 const char *qdata,
1325 u_short *rstatus,
1326 int *rsize,
1327 const char **rdata
1328 )
1329 {
1330 return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
1331 rsize, rdata, FALSE);
1332 }
1333
1334
1335 /*
1336 * doqueryex - send a request and process the response, optionally
1337 * displaying error messages for any error responses.
1338 */
1339 int
1340 doqueryex(
1341 int opcode,
1342 associd_t associd,
1343 int auth,
1344 int qsize,
1345 const char *qdata,
1346 u_short *rstatus,
1347 int *rsize,
1348 const char **rdata,
1349 int quiet
1350 )
1351 {
1352 int res;
1353 int done;
1354
1355 /*
1356 * Check to make sure host is open
1357 */
1358 if (!havehost) {
1359 fprintf(stderr, "***No host open, use `host' command\n");
1360 return -1;
1361 }
1362
1363 done = 0;
1364 sequence++;
1365
1366 again:
1367 /*
1368 * send a request
1369 */
1370 res = sendrequest(opcode, associd, auth, qsize, qdata);
1371 if (res != 0)
1372 return res;
1373
1374 /*
1375 * Get the response. If we got a standard error, print a message
1376 */
1377 res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1378
1379 if (res > 0) {
1380 if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1381 if (res == ERR_INCOMPLETE) {
1382 /*
1383 * better bump the sequence so we don't
1384 * get confused about differing fragments.
1385 */
1386 sequence++;
1387 }
1388 done = 1;
1389 goto again;
1390 }
1391 if (!quiet)
1392 show_error_msg(res, associd);
1393
1394 }
1395 return res;
1396 }
1397
1398
1399 #ifndef BUILD_AS_LIB
1400 /*
1401 * getcmds - read commands from the standard input and execute them
1402 */
1403 static void
1404 getcmds(void)
1405 {
1406 char * line;
1407 int count;
1408
1409 ntp_readline_init(interactive ? prompt : NULL);
1410
1411 for (;;) {
1412 line = ntp_readline(&count);
1413 if (NULL == line)
1414 break;
1415 docmd(line);
1416 free(line);
1417 }
1418
1419 ntp_readline_uninit();
1420 }
1421 #endif /* !BUILD_AS_LIB */
1422
1423
1424 #if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
1425 /*
1426 * abortcmd - catch interrupts and abort the current command
1427 */
1428 static RETSIGTYPE
1429 abortcmd(
1430 int sig
1431 )
1432 {
1433 if (current_output == stdout)
1434 (void) fflush(stdout);
1435 putc('\n', stderr);
1436 (void) fflush(stderr);
1437 if (jump) longjmp(interrupt_buf, 1);
1438 }
1439 #endif /* !SYS_WINNT && !BUILD_AS_LIB */
1440
1441
1442 #ifndef BUILD_AS_LIB
1443 /*
1444 * docmd - decode the command line and execute a command
1445 */
1446 static void
1447 docmd(
1448 const char *cmdline
1449 )
1450 {
1451 char *tokens[1+MAXARGS+2];
1452 struct parse pcmd;
1453 int ntok;
1454 static int i;
1455 struct xcmd *xcmd;
1456
1457 /*
1458 * Tokenize the command line. If nothing on it, return.
1459 */
1460 tokenize(cmdline, tokens, &ntok);
1461 if (ntok == 0)
1462 return;
1463
1464 /*
1465 * Find the appropriate command description.
1466 */
1467 i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1468 if (i == 0) {
1469 (void) fprintf(stderr, "***Command `%s' unknown\n",
1470 tokens[0]);
1471 return;
1472 } else if (i >= 2) {
1473 (void) fprintf(stderr, "***Command `%s' ambiguous\n",
1474 tokens[0]);
1475 return;
1476 }
1477
1478 /* Warn about ignored extra args */
1479 for (i = MAXARGS + 1; i < ntok ; ++i) {
1480 fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
1481 }
1482
1483 /*
1484 * Save the keyword, then walk through the arguments, interpreting
1485 * as we go.
1486 */
1487 pcmd.keyword = tokens[0];
1488 pcmd.nargs = 0;
1489 for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1490 if ((i+1) >= ntok) {
1491 if (!(xcmd->arg[i] & OPT)) {
1492 printusage(xcmd, stderr);
1493 return;
1494 }
1495 break;
1496 }
1497 if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1498 break;
1499 if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1500 return;
1501 pcmd.nargs++;
1502 }
1503
1504 i++;
1505 if (i < ntok && *tokens[i] == '>') {
1506 char *fname;
1507
1508 if (*(tokens[i]+1) != '\0')
1509 fname = tokens[i]+1;
1510 else if ((i+1) < ntok)
1511 fname = tokens[i+1];
1512 else {
1513 (void) fprintf(stderr, "***No file for redirect\n");
1514 return;
1515 }
1516
1517 current_output = fopen(fname, "w");
1518 if (current_output == NULL) {
1519 (void) fprintf(stderr, "***Error opening %s: ", fname);
1520 perror("");
1521 return;
1522 }
1523 i = 1; /* flag we need a close */
1524 } else {
1525 current_output = stdout;
1526 i = 0; /* flag no close */
1527 }
1528
1529 if (interactive && setjmp(interrupt_buf)) {
1530 jump = 0;
1531 return;
1532 } else {
1533 jump++;
1534 (xcmd->handler)(&pcmd, current_output);
1535 jump = 0; /* HMS: 961106: was after fclose() */
1536 if (i) (void) fclose(current_output);
1537 }
1538
1539 return;
1540 }
1541
1542
1543 /*
1544 * tokenize - turn a command line into tokens
1545 *
1546 * SK: Modified to allow a quoted string
1547 *
1548 * HMS: If the first character of the first token is a ':' then (after
1549 * eating inter-token whitespace) the 2nd token is the rest of the line.
1550 */
1551
1552 static void
1553 tokenize(
1554 const char *line,
1555 char **tokens,
1556 int *ntok
1557 )
1558 {
1559 register const char *cp;
1560 register char *sp;
1561 static char tspace[MAXLINE];
1562
1563 sp = tspace;
1564 cp = line;
1565 for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1566 tokens[*ntok] = sp;
1567
1568 /* Skip inter-token whitespace */
1569 while (ISSPACE(*cp))
1570 cp++;
1571
1572 /* If we're at EOL we're done */
1573 if (ISEOL(*cp))
1574 break;
1575
1576 /* If this is the 2nd token and the first token begins
1577 * with a ':', then just grab to EOL.
1578 */
1579
1580 if (*ntok == 1 && tokens[0][0] == ':') {
1581 do {
1582 if (sp - tspace >= MAXLINE)
1583 goto toobig;
1584 *sp++ = *cp++;
1585 } while (!ISEOL(*cp));
1586 }
1587
1588 /* Check if this token begins with a double quote.
1589 * If yes, continue reading till the next double quote
1590 */
1591 else if (*cp == '\"') {
1592 ++cp;
1593 do {
1594 if (sp - tspace >= MAXLINE)
1595 goto toobig;
1596 *sp++ = *cp++;
1597 } while ((*cp != '\"') && !ISEOL(*cp));
1598 /* HMS: a missing closing " should be an error */
1599 }
1600 else {
1601 do {
1602 if (sp - tspace >= MAXLINE)
1603 goto toobig;
1604 *sp++ = *cp++;
1605 } while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1606 /* HMS: Why check for a " in the previous line? */
1607 }
1608
1609 if (sp - tspace >= MAXLINE)
1610 goto toobig;
1611 *sp++ = '\0';
1612 }
1613 return;
1614
1615 toobig:
1616 *ntok = 0;
1617 fprintf(stderr,
1618 "***Line `%s' is too big\n",
1619 line);
1620 return;
1621 }
1622
1623
1624 /*
1625 * getarg - interpret an argument token
1626 */
1627 static int
1628 getarg(
1629 const char *str,
1630 int code,
1631 arg_v *argp
1632 )
1633 {
1634 u_long ul;
1635
1636 switch (code & ~OPT) {
1637 case NTP_STR:
1638 argp->string = str;
1639 break;
1640
1641 case NTP_ADD:
1642 if (!getnetnum(str, &argp->netnum, NULL, 0))
1643 return 0;
1644 break;
1645
1646 case NTP_UINT:
1647 if ('&' == str[0]) {
1648 if (!atouint(&str[1], &ul)) {
1649 fprintf(stderr,
1650 "***Association index `%s' invalid/undecodable\n",
1651 str);
1652 return 0;
1653 }
1654 if (0 == numassoc) {
1655 dogetassoc(stdout);
1656 if (0 == numassoc) {
1657 fprintf(stderr,
1658 "***No associations found, `%s' unknown\n",
1659 str);
1660 return 0;
1661 }
1662 }
1663 ul = min(ul, numassoc);
1664 argp->uval = assoc_cache[ul - 1].assid;
1665 break;
1666 }
1667 if (!atouint(str, &argp->uval)) {
1668 fprintf(stderr, "***Illegal unsigned value %s\n",
1669 str);
1670 return 0;
1671 }
1672 break;
1673
1674 case NTP_INT:
1675 if (!atoint(str, &argp->ival)) {
1676 fprintf(stderr, "***Illegal integer value %s\n",
1677 str);
1678 return 0;
1679 }
1680 break;
1681
1682 case IP_VERSION:
1683 if (!strcmp("-6", str)) {
1684 argp->ival = 6;
1685 } else if (!strcmp("-4", str)) {
1686 argp->ival = 4;
1687 } else {
1688 fprintf(stderr, "***Version must be either 4 or 6\n");
1689 return 0;
1690 }
1691 break;
1692 }
1693
1694 return 1;
1695 }
1696 #endif /* !BUILD_AS_LIB */
1697
1698
1699 /*
1700 * findcmd - find a command in a command description table
1701 */
1702 static int
1703 findcmd(
1704 const char * str,
1705 struct xcmd * clist1,
1706 struct xcmd * clist2,
1707 struct xcmd ** cmd
1708 )
1709 {
1710 struct xcmd *cl;
1711 int clen;
1712 int nmatch;
1713 struct xcmd *nearmatch = NULL;
1714 struct xcmd *clist;
1715
1716 clen = strlen(str);
1717 nmatch = 0;
1718 if (clist1 != 0)
1719 clist = clist1;
1720 else if (clist2 != 0)
1721 clist = clist2;
1722 else
1723 return 0;
1724
1725 again:
1726 for (cl = clist; cl->keyword != 0; cl++) {
1727 /* do a first character check, for efficiency */
1728 if (*str != *(cl->keyword))
1729 continue;
1730 if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1731 /*
1732 * Could be extact match, could be approximate.
1733 * Is exact if the length of the keyword is the
1734 * same as the str.
1735 */
1736 if (*((cl->keyword) + clen) == '\0') {
1737 *cmd = cl;
1738 return 1;
1739 }
1740 nmatch++;
1741 nearmatch = cl;
1742 }
1743 }
1744
1745 /*
1746 * See if there is more to do. If so, go again. Sorry about the
1747 * goto, too much looking at BSD sources...
1748 */
1749 if (clist == clist1 && clist2 != 0) {
1750 clist = clist2;
1751 goto again;
1752 }
1753
1754 /*
1755 * If we got extactly 1 near match, use it, else return number
1756 * of matches.
1757 */
1758 if (nmatch == 1) {
1759 *cmd = nearmatch;
1760 return 1;
1761 }
1762 return nmatch;
1763 }
1764
1765
1766 /*
1767 * getnetnum - given a host name, return its net number
1768 * and (optional) full name
1769 */
1770 int
1771 getnetnum(
1772 const char *hname,
1773 sockaddr_u *num,
1774 char *fullhost,
1775 int af
1776 )
1777 {
1778 struct addrinfo hints, *ai = NULL;
1779
1780 ZERO(hints);
1781 hints.ai_flags = AI_CANONNAME;
1782 #ifdef AI_ADDRCONFIG
1783 hints.ai_flags |= AI_ADDRCONFIG;
1784 #endif
1785
1786 /*
1787 * decodenetnum only works with addresses, but handles syntax
1788 * that getaddrinfo doesn't: [2001::1]:1234
1789 */
1790 if (decodenetnum(hname, num)) {
1791 if (fullhost != NULL)
1792 getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1793 LENHOSTNAME, NULL, 0, 0);
1794 return 1;
1795 } else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1796 INSIST(sizeof(*num) >= ai->ai_addrlen);
1797 memcpy(num, ai->ai_addr, ai->ai_addrlen);
1798 if (fullhost != NULL) {
1799 if (ai->ai_canonname != NULL)
1800 strlcpy(fullhost, ai->ai_canonname,
1801 LENHOSTNAME);
1802 else
1803 getnameinfo(&num->sa, SOCKLEN(num),
1804 fullhost, LENHOSTNAME, NULL,
1805 0, 0);
1806 }
1807 freeaddrinfo(ai);
1808 return 1;
1809 }
1810 fprintf(stderr, "***Can't find host %s\n", hname);
1811
1812 return 0;
1813 }
1814
1815
1816 /*
1817 * nntohost - convert network number to host name. This routine enforces
1818 * the showhostnames setting.
1819 */
1820 const char *
1821 nntohost(
1822 sockaddr_u *netnum
1823 )
1824 {
1825 return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
1826 }
1827
1828
1829 /*
1830 * nntohost_col - convert network number to host name in fixed width.
1831 * This routine enforces the showhostnames setting.
1832 * When displaying hostnames longer than the width,
1833 * the first part of the hostname is displayed. When
1834 * displaying numeric addresses longer than the width,
1835 * Such as IPv6 addresses, the caller decides whether
1836 * the first or last of the numeric address is used.
1837 */
1838 const char *
1839 nntohost_col(
1840 sockaddr_u * addr,
1841 size_t width,
1842 int preserve_lowaddrbits
1843 )
1844 {
1845 const char * out;
1846
1847 if (!showhostnames || SOCK_UNSPEC(addr)) {
1848 if (preserve_lowaddrbits)
1849 out = trunc_left(stoa(addr), width);
1850 else
1851 out = trunc_right(stoa(addr), width);
1852 } else if (ISREFCLOCKADR(addr)) {
1853 out = refnumtoa(addr);
1854 } else {
1855 out = trunc_right(socktohost(addr), width);
1856 }
1857 return out;
1858 }
1859
1860
1861 /*
1862 * nntohostp() is the same as nntohost() plus a :port suffix
1863 */
1864 const char *
1865 nntohostp(
1866 sockaddr_u *netnum
1867 )
1868 {
1869 const char * hostn;
1870 char * buf;
1871
1872 if (!showhostnames || SOCK_UNSPEC(netnum))
1873 return sptoa(netnum);
1874 else if (ISREFCLOCKADR(netnum))
1875 return refnumtoa(netnum);
1876
1877 hostn = socktohost(netnum);
1878 LIB_GETBUF(buf);
1879 snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
1880
1881 return buf;
1882 }
1883
1884 /*
1885 * rtdatetolfp - decode an RT-11 date into an l_fp
1886 */
1887 static int
1888 rtdatetolfp(
1889 char *str,
1890 l_fp *lfp
1891 )
1892 {
1893 register char *cp;
1894 register int i;
1895 struct calendar cal;
1896 char buf[4];
1897
1898 cal.yearday = 0;
1899
1900 /*
1901 * An RT-11 date looks like:
1902 *
1903 * d[d]-Mth-y[y] hh:mm:ss
1904 *
1905 * (No docs, but assume 4-digit years are also legal...)
1906 *
1907 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
1908 */
1909 cp = str;
1910 if (!isdigit((int)*cp)) {
1911 if (*cp == '-') {
1912 /*
1913 * Catch special case
1914 */
1915 L_CLR(lfp);
1916 return 1;
1917 }
1918 return 0;
1919 }
1920
1921 cal.monthday = (u_char) (*cp++ - '0'); /* ascii dependent */
1922 if (isdigit((int)*cp)) {
1923 cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
1924 cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
1925 }
1926
1927 if (*cp++ != '-')
1928 return 0;
1929
1930 for (i = 0; i < 3; i++)
1931 buf[i] = *cp++;
1932 buf[3] = '\0';
1933
1934 for (i = 0; i < 12; i++)
1935 if (STREQ(buf, months[i]))
1936 break;
1937 if (i == 12)
1938 return 0;
1939 cal.month = (u_char)(i + 1);
1940
1941 if (*cp++ != '-')
1942 return 0;
1943
1944 if (!isdigit((int)*cp))
1945 return 0;
1946 cal.year = (u_short)(*cp++ - '0');
1947 if (isdigit((int)*cp)) {
1948 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1949 cal.year = (u_short)(*cp++ - '0');
1950 }
1951 if (isdigit((int)*cp)) {
1952 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1953 cal.year = (u_short)(cal.year + *cp++ - '0');
1954 }
1955 if (isdigit((int)*cp)) {
1956 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1957 cal.year = (u_short)(cal.year + *cp++ - '0');
1958 }
1959
1960 /*
1961 * Catch special case. If cal.year == 0 this is a zero timestamp.
1962 */
1963 if (cal.year == 0) {
1964 L_CLR(lfp);
1965 return 1;
1966 }
1967
1968 if (*cp++ != ' ' || !isdigit((int)*cp))
1969 return 0;
1970 cal.hour = (u_char)(*cp++ - '0');
1971 if (isdigit((int)*cp)) {
1972 cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
1973 cal.hour = (u_char)(cal.hour + *cp++ - '0');
1974 }
1975
1976 if (*cp++ != ':' || !isdigit((int)*cp))
1977 return 0;
1978 cal.minute = (u_char)(*cp++ - '0');
1979 if (isdigit((int)*cp)) {
1980 cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
1981 cal.minute = (u_char)(cal.minute + *cp++ - '0');
1982 }
1983
1984 if (*cp++ != ':' || !isdigit((int)*cp))
1985 return 0;
1986 cal.second = (u_char)(*cp++ - '0');
1987 if (isdigit((int)*cp)) {
1988 cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
1989 cal.second = (u_char)(cal.second + *cp++ - '0');
1990 }
1991
1992 /*
1993 * For RT-11, 1972 seems to be the pivot year
1994 */
1995 if (cal.year < 72)
1996 cal.year += 2000;
1997 if (cal.year < 100)
1998 cal.year += 1900;
1999
2000 lfp->l_ui = caltontp(&cal);
2001 lfp->l_uf = 0;
2002 return 1;
2003 }
2004
2005
2006 /*
2007 * decodets - decode a timestamp into an l_fp format number, with
2008 * consideration of fuzzball formats.
2009 */
2010 int
2011 decodets(
2012 char *str,
2013 l_fp *lfp
2014 )
2015 {
2016 char *cp;
2017 char buf[30];
2018 size_t b;
2019
2020 /*
2021 * If it starts with a 0x, decode as hex.
2022 */
2023 if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2024 return hextolfp(str+2, lfp);
2025
2026 /*
2027 * If it starts with a '"', try it as an RT-11 date.
2028 */
2029 if (*str == '"') {
2030 cp = str + 1;
2031 b = 0;
2032 while ('"' != *cp && '\0' != *cp &&
2033 b < COUNTOF(buf) - 1)
2034 buf[b++] = *cp++;
2035 buf[b] = '\0';
2036 return rtdatetolfp(buf, lfp);
2037 }
2038
2039 /*
2040 * Might still be hex. Check out the first character. Talk
2041 * about heuristics!
2042 */
2043 if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2044 return hextolfp(str, lfp);
2045
2046 /*
2047 * Try it as a decimal. If this fails, try as an unquoted
2048 * RT-11 date. This code should go away eventually.
2049 */
2050 if (atolfp(str, lfp))
2051 return 1;
2052
2053 return rtdatetolfp(str, lfp);
2054 }
2055
2056
2057 /*
2058 * decodetime - decode a time value. It should be in milliseconds
2059 */
2060 int
2061 decodetime(
2062 char *str,
2063 l_fp *lfp
2064 )
2065 {
2066 return mstolfp(str, lfp);
2067 }
2068
2069
2070 /*
2071 * decodeint - decode an integer
2072 */
2073 int
2074 decodeint(
2075 char *str,
2076 long *val
2077 )
2078 {
2079 if (*str == '0') {
2080 if (*(str+1) == 'x' || *(str+1) == 'X')
2081 return hextoint(str+2, (u_long *)val);
2082 return octtoint(str, (u_long *)val);
2083 }
2084 return atoint(str, val);
2085 }
2086
2087
2088 /*
2089 * decodeuint - decode an unsigned integer
2090 */
2091 int
2092 decodeuint(
2093 char *str,
2094 u_long *val
2095 )
2096 {
2097 if (*str == '0') {
2098 if (*(str + 1) == 'x' || *(str + 1) == 'X')
2099 return (hextoint(str + 2, val));
2100 return (octtoint(str, val));
2101 }
2102 return (atouint(str, val));
2103 }
2104
2105
2106 /*
2107 * decodearr - decode an array of time values
2108 */
2109 static int
2110 decodearr(
2111 char *str,
2112 int *narr,
2113 l_fp *lfparr
2114 )
2115 {
2116 register char *cp, *bp;
2117 register l_fp *lfp;
2118 char buf[60];
2119
2120 lfp = lfparr;
2121 cp = str;
2122 *narr = 0;
2123
2124 while (*narr < 8) {
2125 while (isspace((int)*cp))
2126 cp++;
2127 if (*cp == '\0')
2128 break;
2129
2130 bp = buf;
2131 while (!isspace((int)*cp) && *cp != '\0')
2132 *bp++ = *cp++;
2133 *bp++ = '\0';
2134
2135 if (!decodetime(buf, lfp))
2136 return 0;
2137 (*narr)++;
2138 lfp++;
2139 }
2140 return 1;
2141 }
2142
2143
2144 /*
2145 * Finally, the built in command handlers
2146 */
2147
2148 /*
2149 * help - tell about commands, or details of a particular command
2150 */
2151 static void
2152 help(
2153 struct parse *pcmd,
2154 FILE *fp
2155 )
2156 {
2157 struct xcmd *xcp = NULL; /* quiet warning */
2158 const char *cmd;
2159 const char *list[100];
2160 size_t word, words;
2161 size_t row, rows;
2162 size_t col, cols;
2163 size_t length;
2164
2165 if (pcmd->nargs == 0) {
2166 words = 0;
2167 for (xcp = builtins; xcp->keyword != NULL; xcp++) {
2168 if (*(xcp->keyword) != '?' &&
2169 words < COUNTOF(list))
2170 list[words++] = xcp->keyword;
2171 }
2172 for (xcp = opcmds; xcp->keyword != NULL; xcp++)
2173 if (words < COUNTOF(list))
2174 list[words++] = xcp->keyword;
2175
2176 qsort((void *)list, words, sizeof(list[0]), helpsort);
2177 col = 0;
2178 for (word = 0; word < words; word++) {
2179 length = strlen(list[word]);
2180 col = max(col, length);
2181 }
2182
2183 cols = SCREENWIDTH / ++col;
2184 rows = (words + cols - 1) / cols;
2185
2186 fprintf(fp, "ntpq commands:\n");
2187
2188 for (row = 0; row < rows; row++) {
2189 for (word = row; word < words; word += rows)
2190 fprintf(fp, "%-*.*s", (int)col,
2191 (int)col - 1, list[word]);
2192 fprintf(fp, "\n");
2193 }
2194 } else {
2195 cmd = pcmd->argval[0].string;
2196 words = findcmd(cmd, builtins, opcmds, &xcp);
2197 if (words == 0) {
2198 fprintf(stderr,
2199 "Command `%s' is unknown\n", cmd);
2200 return;
2201 } else if (words >= 2) {
2202 fprintf(stderr,
2203 "Command `%s' is ambiguous\n", cmd);
2204 return;
2205 }
2206 fprintf(fp, "function: %s\n", xcp->comment);
2207 printusage(xcp, fp);
2208 }
2209 }
2210
2211
2212 /*
2213 * helpsort - do hostname qsort comparisons
2214 */
2215 static int
2216 helpsort(
2217 const void *t1,
2218 const void *t2
2219 )
2220 {
2221 const char * const * name1 = t1;
2222 const char * const * name2 = t2;
2223
2224 return strcmp(*name1, *name2);
2225 }
2226
2227
2228 /*
2229 * printusage - print usage information for a command
2230 */
2231 static void
2232 printusage(
2233 struct xcmd *xcp,
2234 FILE *fp
2235 )
2236 {
2237 register int i;
2238
2239 /* XXX: Do we need to warn about extra args here too? */
2240
2241 (void) fprintf(fp, "usage: %s", xcp->keyword);
2242 for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2243 if (xcp->arg[i] & OPT)
2244 (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2245 else
2246 (void) fprintf(fp, " %s", xcp->desc[i]);
2247 }
2248 (void) fprintf(fp, "\n");
2249 }
2250
2251
2252 /*
2253 * timeout - set time out time
2254 */
2255 static void
2256 timeout(
2257 struct parse *pcmd,
2258 FILE *fp
2259 )
2260 {
2261 int val;
2262
2263 if (pcmd->nargs == 0) {
2264 val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2265 (void) fprintf(fp, "primary timeout %d ms\n", val);
2266 } else {
2267 tvout.tv_sec = pcmd->argval[0].uval / 1000;
2268 tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
2269 * 1000;
2270 }
2271 }
2272
2273
2274 /*
2275 * auth_delay - set delay for auth requests
2276 */
2277 static void
2278 auth_delay(
2279 struct parse *pcmd,
2280 FILE *fp
2281 )
2282 {
2283 int isneg;
2284 u_long val;
2285
2286 if (pcmd->nargs == 0) {
2287 val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2288 (void) fprintf(fp, "delay %lu ms\n", val);
2289 } else {
2290 if (pcmd->argval[0].ival < 0) {
2291 isneg = 1;
2292 val = (u_long)(-pcmd->argval[0].ival);
2293 } else {
2294 isneg = 0;
2295 val = (u_long)pcmd->argval[0].ival;
2296 }
2297
2298 delay_time.l_ui = val / 1000;
2299 val %= 1000;
2300 delay_time.l_uf = val * 4294967; /* 2**32/1000 */
2301
2302 if (isneg)
2303 L_NEG(&delay_time);
2304 }
2305 }
2306
2307
2308 /*
2309 * host - set the host we are dealing with.
2310 */
2311 static void
2312 host(
2313 struct parse *pcmd,
2314 FILE *fp
2315 )
2316 {
2317 int i;
2318
2319 if (pcmd->nargs == 0) {
2320 if (havehost)
2321 (void) fprintf(fp, "current host is %s\n",
2322 currenthost);
2323 else
2324 (void) fprintf(fp, "no current host\n");
2325 return;
2326 }
2327
2328 i = 0;
2329 ai_fam_templ = ai_fam_default;
2330 if (pcmd->nargs == 2) {
2331 if (!strcmp("-4", pcmd->argval[i].string))
2332 ai_fam_templ = AF_INET;
2333 else if (!strcmp("-6", pcmd->argval[i].string))
2334 ai_fam_templ = AF_INET6;
2335 else
2336 goto no_change;
2337 i = 1;
2338 }
2339 if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
2340 fprintf(fp, "current host set to %s\n", currenthost);
2341 } else {
2342 no_change:
2343 if (havehost)
2344 fprintf(fp, "current host remains %s\n",
2345 currenthost);
2346 else
2347 fprintf(fp, "still no current host\n");
2348 }
2349 }
2350
2351
2352 /*
2353 * poll - do one (or more) polls of the host via NTP
2354 */
2355 /*ARGSUSED*/
2356 static void
2357 ntp_poll(
2358 struct parse *pcmd,
2359 FILE *fp
2360 )
2361 {
2362 (void) fprintf(fp, "poll not implemented yet\n");
2363 }
2364
2365
2366 /*
2367 * keyid - get a keyid to use for authenticating requests
2368 */
2369 static void
2370 keyid(
2371 struct parse *pcmd,
2372 FILE *fp
2373 )
2374 {
2375 if (pcmd->nargs == 0) {
2376 if (info_auth_keyid == 0)
2377 (void) fprintf(fp, "no keyid defined\n");
2378 else
2379 (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
2380 } else {
2381 /* allow zero so that keyid can be cleared. */
2382 if(pcmd->argval[0].uval > NTP_MAXKEY)
2383 (void) fprintf(fp, "Invalid key identifier\n");
2384 info_auth_keyid = pcmd->argval[0].uval;
2385 }
2386 }
2387
2388 /*
2389 * keytype - get type of key to use for authenticating requests
2390 */
2391 static void
2392 keytype(
2393 struct parse *pcmd,
2394 FILE *fp
2395 )
2396 {
2397 const char * digest_name;
2398 size_t digest_len;
2399 int key_type;
2400
2401 if (!pcmd->nargs) {
2402 fprintf(fp, "keytype is %s with %lu octet digests\n",
2403 keytype_name(info_auth_keytype),
2404 (u_long)info_auth_hashlen);
2405 return;
2406 }
2407
2408 digest_name = pcmd->argval[0].string;
2409 digest_len = 0;
2410 key_type = keytype_from_text(digest_name, &digest_len);
2411
2412 if (!key_type) {
2413 fprintf(fp, "keytype must be 'md5'%s\n",
2414 #ifdef OPENSSL
2415 " or a digest type provided by OpenSSL");
2416 #else
2417 "");
2418 #endif
2419 return;
2420 }
2421
2422 info_auth_keytype = key_type;
2423 info_auth_hashlen = digest_len;
2424 }
2425
2426
2427 /*
2428 * passwd - get an authentication key
2429 */
2430 /*ARGSUSED*/
2431 static void
2432 passwd(
2433 struct parse *pcmd,
2434 FILE *fp
2435 )
2436 {
2437 const char *pass;
2438
2439 if (info_auth_keyid == 0) {
2440 info_auth_keyid = getkeyid("Keyid: ");
2441 if (info_auth_keyid == 0) {
2442 (void)fprintf(fp, "Keyid must be defined\n");
2443 return;
2444 }
2445 }
2446 if (pcmd->nargs >= 1)
2447 pass = pcmd->argval[0].string;
2448 else {
2449 pass = getpass_keytype(info_auth_keytype);
2450 if ('\0' == pass[0]) {
2451 fprintf(fp, "Password unchanged\n");
2452 return;
2453 }
2454 }
2455 authusekey(info_auth_keyid, info_auth_keytype,
2456 (const u_char *)pass);
2457 authtrust(info_auth_keyid, 1);
2458 }
2459
2460
2461 /*
2462 * hostnames - set the showhostnames flag
2463 */
2464 static void
2465 hostnames(
2466 struct parse *pcmd,
2467 FILE *fp
2468 )
2469 {
2470 if (pcmd->nargs == 0) {
2471 if (showhostnames)
2472 (void) fprintf(fp, "hostnames being shown\n");
2473 else
2474 (void) fprintf(fp, "hostnames not being shown\n");
2475 } else {
2476 if (STREQ(pcmd->argval[0].string, "yes"))
2477 showhostnames = 1;
2478 else if (STREQ(pcmd->argval[0].string, "no"))
2479 showhostnames = 0;
2480 else
2481 (void)fprintf(stderr, "What?\n");
2482 }
2483 }
2484
2485
2486
2487 /*
2488 * setdebug - set/change debugging level
2489 */
2490 static void
2491 setdebug(
2492 struct parse *pcmd,
2493 FILE *fp
2494 )
2495 {
2496 if (pcmd->nargs == 0) {
2497 (void) fprintf(fp, "debug level is %d\n", debug);
2498 return;
2499 } else if (STREQ(pcmd->argval[0].string, "no")) {
2500 debug = 0;
2501 } else if (STREQ(pcmd->argval[0].string, "more")) {
2502 debug++;
2503 } else if (STREQ(pcmd->argval[0].string, "less")) {
2504 debug--;
2505 } else {
2506 (void) fprintf(fp, "What?\n");
2507 return;
2508 }
2509 (void) fprintf(fp, "debug level set to %d\n", debug);
2510 }
2511
2512
2513 /*
2514 * quit - stop this nonsense
2515 */
2516 /*ARGSUSED*/
2517 static void
2518 quit(
2519 struct parse *pcmd,
2520 FILE *fp
2521 )
2522 {
2523 if (havehost)
2524 closesocket(sockfd); /* cleanliness next to godliness */
2525 exit(0);
2526 }
2527
2528
2529 /*
2530 * version - print the current version number
2531 */
2532 /*ARGSUSED*/
2533 static void
2534 version(
2535 struct parse *pcmd,
2536 FILE *fp
2537 )
2538 {
2539
2540 (void) fprintf(fp, "%s\n", Version);
2541 return;
2542 }
2543
2544
2545 /*
2546 * raw - set raw mode output
2547 */
2548 /*ARGSUSED*/
2549 static void
2550 raw(
2551 struct parse *pcmd,
2552 FILE *fp
2553 )
2554 {
2555 rawmode = 1;
2556 (void) fprintf(fp, "Output set to raw\n");
2557 }
2558
2559
2560 /*
2561 * cooked - set cooked mode output
2562 */
2563 /*ARGSUSED*/
2564 static void
2565 cooked(
2566 struct parse *pcmd,
2567 FILE *fp
2568 )
2569 {
2570 rawmode = 0;
2571 (void) fprintf(fp, "Output set to cooked\n");
2572 return;
2573 }
2574
2575
2576 /*
2577 * authenticate - always authenticate requests to this host
2578 */
2579 static void
2580 authenticate(
2581 struct parse *pcmd,
2582 FILE *fp
2583 )
2584 {
2585 if (pcmd->nargs == 0) {
2586 if (always_auth) {
2587 (void) fprintf(fp,
2588 "authenticated requests being sent\n");
2589 } else
2590 (void) fprintf(fp,
2591 "unauthenticated requests being sent\n");
2592 } else {
2593 if (STREQ(pcmd->argval[0].string, "yes")) {
2594 always_auth = 1;
2595 } else if (STREQ(pcmd->argval[0].string, "no")) {
2596 always_auth = 0;
2597 } else
2598 (void)fprintf(stderr, "What?\n");
2599 }
2600 }
2601
2602
2603 /*
2604 * ntpversion - choose the NTP version to use
2605 */
2606 static void
2607 ntpversion(
2608 struct parse *pcmd,
2609 FILE *fp
2610 )
2611 {
2612 if (pcmd->nargs == 0) {
2613 (void) fprintf(fp,
2614 "NTP version being claimed is %d\n", pktversion);
2615 } else {
2616 if (pcmd->argval[0].uval < NTP_OLDVERSION
2617 || pcmd->argval[0].uval > NTP_VERSION) {
2618 (void) fprintf(stderr, "versions %d to %d, please\n",
2619 NTP_OLDVERSION, NTP_VERSION);
2620 } else {
2621 pktversion = (u_char) pcmd->argval[0].uval;
2622 }
2623 }
2624 }
2625
2626
2627 static void __attribute__((__format__(__printf__, 1, 0)))
2628 vwarning(const char *fmt, va_list ap)
2629 {
2630 int serrno = errno;
2631 (void) fprintf(stderr, "%s: ", progname);
2632 vfprintf(stderr, fmt, ap);
2633 (void) fprintf(stderr, ": %s", strerror(serrno));
2634 }
2635
2636 /*
2637 * warning - print a warning message
2638 */
2639 static void __attribute__((__format__(__printf__, 1, 2)))
2640 warning(
2641 const char *fmt,
2642 ...
2643 )
2644 {
2645 va_list ap;
2646 va_start(ap, fmt);
2647 vwarning(fmt, ap);
2648 va_end(ap);
2649 }
2650
2651
2652 /*
2653 * error - print a message and exit
2654 */
2655 static void __attribute__((__format__(__printf__, 1, 2)))
2656 error(
2657 const char *fmt,
2658 ...
2659 )
2660 {
2661 va_list ap;
2662 va_start(ap, fmt);
2663 vwarning(fmt, ap);
2664 va_end(ap);
2665 exit(1);
2666 }
2667 /*
2668 * getkeyid - prompt the user for a keyid to use
2669 */
2670 static u_long
2671 getkeyid(
2672 const char *keyprompt
2673 )
2674 {
2675 int c;
2676 FILE *fi;
2677 char pbuf[20];
2678 size_t i;
2679 size_t ilim;
2680
2681 #ifndef SYS_WINNT
2682 if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
2683 #else
2684 if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
2685 #endif /* SYS_WINNT */
2686 fi = stdin;
2687 else
2688 setbuf(fi, (char *)NULL);
2689 fprintf(stderr, "%s", keyprompt); fflush(stderr);
2690 for (i = 0, ilim = COUNTOF(pbuf) - 1;
2691 i < ilim && (c = getc(fi)) != '\n' && c != EOF;
2692 )
2693 pbuf[i++] = (char)c;
2694 pbuf[i] = '\0';
2695 if (fi != stdin)
2696 fclose(fi);
2697
2698 return (u_long) atoi(pbuf);
2699 }
2700
2701
2702 /*
2703 * atoascii - printable-ize possibly ascii data using the character
2704 * transformations cat -v uses.
2705 */
2706 static void
2707 atoascii(
2708 const char *in,
2709 size_t in_octets,
2710 char *out,
2711 size_t out_octets
2712 )
2713 {
2714 const u_char * pchIn;
2715 const u_char * pchInLimit;
2716 u_char * pchOut;
2717 u_char c;
2718
2719 pchIn = (const u_char *)in;
2720 pchInLimit = pchIn + in_octets;
2721 pchOut = (u_char *)out;
2722
2723 if (NULL == pchIn) {
2724 if (0 < out_octets)
2725 *pchOut = '\0';
2726 return;
2727 }
2728
2729 #define ONEOUT(c) \
2730 do { \
2731 if (0 == --out_octets) { \
2732 *pchOut = '\0'; \
2733 return; \
2734 } \
2735 *pchOut++ = (c); \
2736 } while (0)
2737
2738 for ( ; pchIn < pchInLimit; pchIn++) {
2739 c = *pchIn;
2740 if ('\0' == c)
2741 break;
2742 if (c & 0x80) {
2743 ONEOUT('M');
2744 ONEOUT('-');
2745 c &= 0x7f;
2746 }
2747 if (c < ' ') {
2748 ONEOUT('^');
2749 ONEOUT((u_char)(c + '@'));
2750 } else if (0x7f == c) {
2751 ONEOUT('^');
2752 ONEOUT('?');
2753 } else
2754 ONEOUT(c);
2755 }
2756 ONEOUT('\0');
2757
2758 #undef ONEOUT
2759 }
2760
2761
2762 /*
2763 * makeascii - print possibly ascii data using the character
2764 * transformations that cat -v uses.
2765 */
2766 void
2767 makeascii(
2768 int length,
2769 const char *data,
2770 FILE *fp
2771 )
2772 {
2773 const u_char *data_u_char;
2774 const u_char *cp;
2775 int c;
2776
2777 data_u_char = (const u_char *)data;
2778
2779 for (cp = data_u_char; cp < data_u_char + length; cp++) {
2780 c = (int)*cp;
2781 if (c & 0x80) {
2782 putc('M', fp);
2783 putc('-', fp);
2784 c &= 0x7f;
2785 }
2786
2787 if (c < ' ') {
2788 putc('^', fp);
2789 putc(c + '@', fp);
2790 } else if (0x7f == c) {
2791 putc('^', fp);
2792 putc('?', fp);
2793 } else
2794 putc(c, fp);
2795 }
2796 }
2797
2798
2799 /*
2800 * asciize - same thing as makeascii except add a newline
2801 */
2802 void
2803 asciize(
2804 int length,
2805 char *data,
2806 FILE *fp
2807 )
2808 {
2809 makeascii(length, data, fp);
2810 putc('\n', fp);
2811 }
2812
2813
2814 /*
2815 * truncate string to fit clipping excess at end.
2816 * "too long" -> "too l"
2817 * Used for hostnames.
2818 */
2819 const char *
2820 trunc_right(
2821 const char * src,
2822 size_t width
2823 )
2824 {
2825 size_t sl;
2826 char * out;
2827
2828
2829 sl = strlen(src);
2830 if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
2831 LIB_GETBUF(out);
2832 memcpy(out, src, width);
2833 out[width] = '\0';
2834
2835 return out;
2836 }
2837
2838 return src;
2839 }
2840
2841
2842 /*
2843 * truncate string to fit by preserving right side and using '_' to hint
2844 * "too long" -> "_long"
2845 * Used for local IPv6 addresses, where low bits differentiate.
2846 */
2847 const char *
2848 trunc_left(
2849 const char * src,
2850 size_t width
2851 )
2852 {
2853 size_t sl;
2854 char * out;
2855
2856
2857 sl = strlen(src);
2858 if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
2859 LIB_GETBUF(out);
2860 out[0] = '_';
2861 memcpy(&out[1], &src[sl + 1 - width], width);
2862
2863 return out;
2864 }
2865
2866 return src;
2867 }
2868
2869
2870 /*
2871 * Some circular buffer space
2872 */
2873 #define CBLEN 80
2874 #define NUMCB 6
2875
2876 char circ_buf[NUMCB][CBLEN];
2877 int nextcb = 0;
2878
2879 /*
2880 * nextvar - find the next variable in the buffer
2881 */
2882 int
2883 nextvar(
2884 int *datalen,
2885 const char **datap,
2886 char **vname,
2887 char **vvalue
2888 )
2889 {
2890 const char *cp;
2891 const char *np;
2892 const char *cpend;
2893 size_t srclen;
2894 size_t len;
2895 static char name[MAXVARLEN];
2896 static char value[MAXVALLEN];
2897
2898 cp = *datap;
2899 cpend = cp + *datalen;
2900
2901 /*
2902 * Space past commas and white space
2903 */
2904 while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
2905 cp++;
2906 if (cp >= cpend)
2907 return 0;
2908
2909 /*
2910 * Copy name until we hit a ',', an '=', a '\r' or a '\n'. Backspace
2911 * over any white space and terminate it.
2912 */
2913 srclen = strcspn(cp, ",=\r\n");
2914 srclen = min(srclen, (size_t)(cpend - cp));
2915 len = srclen;
2916 while (len > 0 && isspace((unsigned char)cp[len - 1]))
2917 len--;
2918 if (len > 0)
2919 memcpy(name, cp, len);
2920 name[len] = '\0';
2921 *vname = name;
2922 cp += srclen;
2923
2924 /*
2925 * Check if we hit the end of the buffer or a ','. If so we are done.
2926 */
2927 if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
2928 if (cp < cpend)
2929 cp++;
2930 *datap = cp;
2931 *datalen = cpend - cp;
2932 *vvalue = NULL;
2933 return 1;
2934 }
2935
2936 /*
2937 * So far, so good. Copy out the value
2938 */
2939 cp++; /* past '=' */
2940 while (cp < cpend && (isspace((unsigned char)*cp) && *cp != '\r' && *cp != '\n'))
2941 cp++;
2942 np = cp;
2943 if ('"' == *np) {
2944 do {
2945 np++;
2946 } while (np < cpend && '"' != *np);
2947 if (np < cpend && '"' == *np)
2948 np++;
2949 } else {
2950 while (np < cpend && ',' != *np && '\r' != *np)
2951 np++;
2952 }
2953 len = np - cp;
2954 if (np > cpend || len >= sizeof(value) ||
2955 (np < cpend && ',' != *np && '\r' != *np))
2956 return 0;
2957 memcpy(value, cp, len);
2958 /*
2959 * Trim off any trailing whitespace
2960 */
2961 while (len > 0 && isspace((unsigned char)value[len - 1]))
2962 len--;
2963 value[len] = '\0';
2964
2965 /*
2966 * Return this. All done.
2967 */
2968 if (np < cpend && ',' == *np)
2969 np++;
2970 *datap = np;
2971 *datalen = cpend - np;
2972 *vvalue = value;
2973 return 1;
2974 }
2975
2976
2977 u_short
2978 varfmt(const char * varname)
2979 {
2980 u_int n;
2981
2982 for (n = 0; n < COUNTOF(cookedvars); n++)
2983 if (!strcmp(varname, cookedvars[n].varname))
2984 return cookedvars[n].fmt;
2985
2986 return PADDING;
2987 }
2988
2989
2990 /*
2991 * printvars - print variables returned in response packet
2992 */
2993 void
2994 printvars(
2995 int length,
2996 const char *data,
2997 int status,
2998 int sttype,
2999 int quiet,
3000 FILE *fp
3001 )
3002 {
3003 if (rawmode)
3004 rawprint(sttype, length, data, status, quiet, fp);
3005 else
3006 cookedprint(sttype, length, data, status, quiet, fp);
3007 }
3008
3009
3010 /*
3011 * rawprint - do a printout of the data in raw mode
3012 */
3013 static void
3014 rawprint(
3015 int datatype,
3016 int length,
3017 const char *data,
3018 int status,
3019 int quiet,
3020 FILE *fp
3021 )
3022 {
3023 const char *cp;
3024 const char *cpend;
3025
3026 /*
3027 * Essentially print the data as is. We reformat unprintables, though.
3028 */
3029 cp = data;
3030 cpend = data + length;
3031
3032 if (!quiet)
3033 (void) fprintf(fp, "status=0x%04x,\n", status);
3034
3035 while (cp < cpend) {
3036 if (*cp == '\r') {
3037 /*
3038 * If this is a \r and the next character is a
3039 * \n, supress this, else pretty print it. Otherwise
3040 * just output the character.
3041 */
3042 if (cp == (cpend - 1) || *(cp + 1) != '\n')
3043 makeascii(1, cp, fp);
3044 } else if (isspace((unsigned char)*cp) || isprint((unsigned char)*cp))
3045 putc(*cp, fp);
3046 else
3047 makeascii(1, cp, fp);
3048 cp++;
3049 }
3050 }
3051
3052
3053 /*
3054 * Global data used by the cooked output routines
3055 */
3056 int out_chars; /* number of characters output */
3057 int out_linecount; /* number of characters output on this line */
3058
3059
3060 /*
3061 * startoutput - get ready to do cooked output
3062 */
3063 static void
3064 startoutput(void)
3065 {
3066 out_chars = 0;
3067 out_linecount = 0;
3068 }
3069
3070
3071 /*
3072 * output - output a variable=value combination
3073 */
3074 static void
3075 output(
3076 FILE *fp,
3077 const char *name,
3078 const char *value
3079 )
3080 {
3081 size_t len;
3082
3083 /* strlen of "name=value" */
3084 len = strlen(name) + 1 + strlen(value);
3085
3086 if (out_chars != 0) {
3087 out_chars += 2;
3088 if ((out_linecount + len + 2) > MAXOUTLINE) {
3089 fputs(",\n", fp);
3090 out_linecount = 0;
3091 } else {
3092 fputs(", ", fp);
3093 out_linecount += 2;
3094 }
3095 }
3096
3097 fputs(name, fp);
3098 putc('=', fp);
3099 fputs(value, fp);
3100 out_chars += len;
3101 out_linecount += len;
3102 }
3103
3104
3105 /*
3106 * endoutput - terminate a block of cooked output
3107 */
3108 static void
3109 endoutput(
3110 FILE *fp
3111 )
3112 {
3113 if (out_chars != 0)
3114 putc('\n', fp);
3115 }
3116
3117
3118 /*
3119 * outputarr - output an array of values
3120 */
3121 static void
3122 outputarr(
3123 FILE *fp,
3124 char *name,
3125 int narr,
3126 l_fp *lfp
3127 )
3128 {
3129 register char *bp;
3130 register char *cp;
3131 register int i;
3132 register int len;
3133 char buf[256];
3134
3135 bp = buf;
3136 /*
3137 * Hack to align delay and offset values
3138 */
3139 for (i = (int)strlen(name); i < 11; i++)
3140 *bp++ = ' ';
3141
3142 for (i = narr; i > 0; i--) {
3143 if (i != narr)
3144 *bp++ = ' ';
3145 cp = lfptoms(lfp, 2);
3146 len = strlen(cp);
3147 if (len > 7) {
3148 cp[7] = '\0';
3149 len = 7;
3150 }
3151 while (len < 7) {
3152 *bp++ = ' ';
3153 len++;
3154 }
3155 while (*cp != '\0')
3156 *bp++ = *cp++;
3157 lfp++;
3158 }
3159 *bp = '\0';
3160 output(fp, name, buf);
3161 }
3162
3163 static char *
3164 tstflags(
3165 u_long val
3166 )
3167 {
3168 register char *cp, *s;
3169 size_t cb;
3170 register int i;
3171 register const char *sep;
3172
3173 sep = "";
3174 i = 0;
3175 s = cp = circ_buf[nextcb];
3176 if (++nextcb >= NUMCB)
3177 nextcb = 0;
3178 cb = sizeof(circ_buf[0]);
3179
3180 snprintf(cp, cb, "%02lx", val);
3181 cp += strlen(cp);
3182 cb -= strlen(cp);
3183 if (!val) {
3184 strlcat(cp, " ok", cb);
3185 cp += strlen(cp);
3186 cb -= strlen(cp);
3187 } else {
3188 if (cb) {
3189 *cp++ = ' ';
3190 cb--;
3191 }
3192 for (i = 0; i < (int)COUNTOF(tstflagnames); i++) {
3193 if (val & 0x1) {
3194 snprintf(cp, cb, "%s%s", sep,
3195 tstflagnames[i]);
3196 sep = ", ";
3197 cp += strlen(cp);
3198 cb -= strlen(cp);
3199 }
3200 val >>= 1;
3201 }
3202 }
3203 if (cb)
3204 *cp = '\0';
3205
3206 return s;
3207 }
3208
3209 /*
3210 * cookedprint - output variables in cooked mode
3211 */
3212 static void
3213 cookedprint(
3214 int datatype,
3215 int length,
3216 const char *data,
3217 int status,
3218 int quiet,
3219 FILE *fp
3220 )
3221 {
3222 char *name;
3223 char *value;
3224 char output_raw;
3225 int fmt;
3226 l_fp lfp;
3227 sockaddr_u hval;
3228 u_long uval;
3229 int narr;
3230 size_t len;
3231 l_fp lfparr[8];
3232 char b[12];
3233 char bn[2 * MAXVARLEN];
3234 char bv[2 * MAXVALLEN];
3235
3236 UNUSED_ARG(datatype);
3237
3238 if (!quiet)
3239 fprintf(fp, "status=%04x %s,\n", status,
3240 statustoa(datatype, status));
3241
3242 startoutput();
3243 while (nextvar(&length, &data, &name, &value)) {
3244 fmt = varfmt(name);
3245 output_raw = 0;
3246 switch (fmt) {
3247
3248 case PADDING:
3249 output_raw = '*';
3250 break;
3251
3252 case TS:
3253 if (!decodets(value, &lfp))
3254 output_raw = '?';
3255 else
3256 output(fp, name, prettydate(&lfp));
3257 break;
3258
3259 case HA: /* fallthru */
3260 case NA:
3261 if (!decodenetnum(value, &hval)) {
3262 output_raw = '?';
3263 } else if (fmt == HA){
3264 output(fp, name, nntohost(&hval));
3265 } else {
3266 output(fp, name, stoa(&hval));
3267 }
3268 break;
3269
3270 case RF:
3271 if (decodenetnum(value, &hval)) {
3272 if (ISREFCLOCKADR(&hval))
3273 output(fp, name,
3274 refnumtoa(&hval));
3275 else
3276 output(fp, name, stoa(&hval));
3277 } else if (strlen(value) <= 4) {
3278 output(fp, name, value);
3279 } else {
3280 output_raw = '?';
3281 }
3282 break;
3283
3284 case LP:
3285 if (!decodeuint(value, &uval) || uval > 3) {
3286 output_raw = '?';
3287 } else {
3288 b[0] = (0x2 & uval)
3289 ? '1'
3290 : '0';
3291 b[1] = (0x1 & uval)
3292 ? '1'
3293 : '0';
3294 b[2] = '\0';
3295 output(fp, name, b);
3296 }
3297 break;
3298
3299 case OC:
3300 if (!decodeuint(value, &uval)) {
3301 output_raw = '?';
3302 } else {
3303 snprintf(b, sizeof(b), "%03lo", uval);
3304 output(fp, name, b);
3305 }
3306 break;
3307
3308 case AR:
3309 if (!decodearr(value, &narr, lfparr))
3310 output_raw = '?';
3311 else
3312 outputarr(fp, name, narr, lfparr);
3313 break;
3314
3315 case FX:
3316 if (!decodeuint(value, &uval))
3317 output_raw = '?';
3318 else
3319 output(fp, name, tstflags(uval));
3320 break;
3321
3322 default:
3323 fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3324 name, value, fmt);
3325 output_raw = '?';
3326 break;
3327 }
3328
3329 if (output_raw != 0) {
3330 atoascii(name, MAXVARLEN, bn, sizeof(bn));
3331 atoascii(value, MAXVALLEN, bv, sizeof(bv));
3332 if (output_raw != '*') {
3333 len = strlen(bv);
3334 bv[len] = output_raw;
3335 bv[len+1] = '\0';
3336 }
3337 output(fp, bn, bv);
3338 }
3339 }
3340 endoutput(fp);
3341 }
3342
3343
3344 /*
3345 * sortassoc - sort associations in the cache into ascending order
3346 */
3347 void
3348 sortassoc(void)
3349 {
3350 if (numassoc > 1)
3351 qsort(assoc_cache, (size_t)numassoc,
3352 sizeof(assoc_cache[0]), &assoccmp);
3353 }
3354
3355
3356 /*
3357 * assoccmp - compare two associations
3358 */
3359 static int
3360 assoccmp(
3361 const void *t1,
3362 const void *t2
3363 )
3364 {
3365 const struct association *ass1 = t1;
3366 const struct association *ass2 = t2;
3367
3368 if (ass1->assid < ass2->assid)
3369 return -1;
3370 if (ass1->assid > ass2->assid)
3371 return 1;
3372 return 0;
3373 }
3374
3375
3376 /*
3377 * grow_assoc_cache() - enlarge dynamic assoc_cache array
3378 *
3379 * The strategy is to add an assumed 4k page size at a time, leaving
3380 * room for malloc() bookkeeping overhead equivalent to 4 pointers.
3381 */
3382 void
3383 grow_assoc_cache(void)
3384 {
3385 static size_t prior_sz;
3386 size_t new_sz;
3387
3388 new_sz = prior_sz + 4 * 1024;
3389 if (0 == prior_sz) {
3390 new_sz -= 4 * sizeof(void *);
3391 }
3392 assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
3393 prior_sz = new_sz;
3394 assoc_cache_slots = new_sz / sizeof(assoc_cache[0]);
3395 }
3396
3397
3398 /*
3399 * ntpq_custom_opt_handler - autoopts handler for -c and -p
3400 *
3401 * By default, autoopts loses the relative order of -c and -p options
3402 * on the command line. This routine replaces the default handler for
3403 * those routines and builds a list of commands to execute preserving
3404 * the order.
3405 */
3406 void
3407 ntpq_custom_opt_handler(
3408 tOptions *pOptions,
3409 tOptDesc *pOptDesc
3410 )
3411 {
3412 switch (pOptDesc->optValue) {
3413
3414 default:
3415 fprintf(stderr,
3416 "ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3417 pOptDesc->optValue, pOptDesc->optValue);
3418 exit(1);
3419
3420 case 'c':
3421 ADDCMD(pOptDesc->pzLastArg);
3422 break;
3423
3424 case 'p':
3425 ADDCMD("peers");
3426 break;
3427 }
3428 }
3429