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