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