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