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