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