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