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