Obfuscate RCS ID matching so that CVS doesn't expand it.
[netbsd-mini2440.git] / dist / ntp / ntpq / ntpq.c
blob7185d71a41e9a7d4fadcb56932debcb6222b51bc
1 /* $NetBSD: ntpq.c,v 1.12 2009/04/17 02:45:55 christos Exp $ */
3 /*
4 * ntpq - query an NTP server using mode 6 commands
5 */
7 #include <stdio.h>
9 #include <ctype.h>
10 #include <signal.h>
11 #include <setjmp.h>
12 #include <sys/types.h>
13 #include <sys/time.h>
15 #include "ntpq.h"
16 #include "ntp_unixtime.h"
17 #include "ntp_calendar.h"
18 #include "ntp_io.h"
19 #include "ntp_select.h"
20 #include "ntp_stdlib.h"
21 /* Don't include ISC's version of IPv6 variables and structures */
22 #define ISC_IPV6_H 1
23 #include "isc/net.h"
24 #include "isc/result.h"
26 #include "ntpq-opts.h"
28 #ifdef SYS_WINNT
29 # include <Mswsock.h>
30 # include <io.h>
31 #else
32 # define closesocket close
33 #endif /* SYS_WINNT */
35 #if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
36 # include <readline/readline.h>
37 # include <readline/history.h>
38 #endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */
40 #ifdef SYS_VXWORKS
41 /* vxWorks needs mode flag -casey*/
42 # define open(name, flags) open(name, flags, 0777)
43 # define SERVER_PORT_NUM 123
44 #endif
46 /* we use COMMAND as an autogen keyword */
47 #ifdef COMMAND
48 # undef COMMAND
49 #endif
52 * Because we potentially understand a lot of commands we will run
53 * interactive if connected to a terminal.
55 int interactive = 0; /* set to 1 when we should prompt */
56 const char *prompt = "ntpq> "; /* prompt to ask him about */
60 * for get_systime()
62 s_char sys_precision; /* local clock precision (log2 s) */
65 * Keyid used for authenticated requests. Obtained on the fly.
67 u_long info_auth_keyid = 0;
70 * Type of key md5
72 #define KEY_TYPE_MD5 4
74 static int info_auth_keytype = KEY_TYPE_MD5; /* MD5 */
75 u_long current_time; /* needed by authkeys; not used */
78 * Flag which indicates we should always send authenticated requests
80 int always_auth = 0;
83 * Flag which indicates raw mode output.
85 int rawmode = 0;
88 * Packet version number we use
90 u_char pktversion = NTP_OLDVERSION + 1;
93 * Don't jump if no set jmp.
95 volatile int jump = 0;
98 * Format values
100 #define PADDING 0
101 #define TS 1 /* time stamp */
102 #define FL 2 /* l_fp type value */
103 #define FU 3 /* u_fp type value */
104 #define FS 4 /* s_fp type value */
105 #define UI 5 /* unsigned integer value */
106 #define SI 6 /* signed integer value */
107 #define HA 7 /* host address */
108 #define NA 8 /* network address */
109 #define ST 9 /* string value */
110 #define RF 10 /* refid (sometimes string, sometimes not) */
111 #define LP 11 /* leap (print in binary) */
112 #define OC 12 /* integer, print in octal */
113 #define MD 13 /* mode */
114 #define AR 14 /* array of times */
115 #define FX 15 /* test flags */
116 #define EOV 255 /* end of table */
120 * System variable values. The array can be indexed by
121 * the variable index to find the textual name.
123 struct ctl_var sys_var[] = {
124 { 0, PADDING, "" }, /* 0 */
125 { CS_LEAP, LP, "leap" }, /* 1 */
126 { CS_STRATUM, UI, "stratum" }, /* 2 */
127 { CS_PRECISION, SI, "precision" }, /* 3 */
128 { CS_ROOTDELAY, FS, "rootdelay" }, /* 4 */
129 { CS_ROOTDISPERSION, FU, "rootdispersion" }, /* 5 */
130 { CS_REFID, RF, "refid" }, /* 6 */
131 { CS_REFTIME, TS, "reftime" }, /* 7 */
132 { CS_POLL, UI, "poll" }, /* 8 */
133 { CS_PEERID, UI, "peer" }, /* 9 */
134 { CS_STATE, UI, "state" }, /* 10 */
135 { CS_OFFSET, FL, "offset" }, /* 11 */
136 { CS_DRIFT, FS, "frequency" }, /* 12 */
137 { CS_JITTER, FU, "jitter" }, /* 13 */
138 { CS_CLOCK, TS, "clock" }, /* 14 */
139 { CS_PROCESSOR, ST, "processor" }, /* 15 */
140 { CS_SYSTEM, ST, "system" }, /* 16 */
141 { CS_VERSION, ST, "version" }, /* 17 */
142 { CS_STABIL, FS, "stability" }, /* 18 */
143 { CS_VARLIST, ST, "sys_var_list" }, /* 19 */
144 { 0, EOV, "" }
149 * Peer variable list
151 struct ctl_var peer_var[] = {
152 { 0, PADDING, "" }, /* 0 */
153 { CP_CONFIG, UI, "config" }, /* 1 */
154 { CP_AUTHENABLE, UI, "authenable" }, /* 2 */
155 { CP_AUTHENTIC, UI, "authentic" }, /* 3 */
156 { CP_SRCADR, HA, "srcadr" }, /* 4 */
157 { CP_SRCPORT, UI, "srcport" }, /* 5 */
158 { CP_DSTADR, NA, "dstadr" }, /* 6 */
159 { CP_DSTPORT, UI, "dstport" }, /* 7 */
160 { CP_LEAP, LP, "leap" }, /* 8 */
161 { CP_HMODE, MD, "hmode" }, /* 9 */
162 { CP_STRATUM, UI, "stratum" }, /* 10 */
163 { CP_PPOLL, UI, "ppoll" }, /* 11 */
164 { CP_HPOLL, UI, "hpoll" }, /* 12 */
165 { CP_PRECISION, SI, "precision" }, /* 13 */
166 { CP_ROOTDELAY, FS, "rootdelay" }, /* 14 */
167 { CP_ROOTDISPERSION, FU, "rootdispersion" }, /* 15 */
168 { CP_REFID, RF, "refid" }, /* 16 */
169 { CP_REFTIME, TS, "reftime" }, /* 17 */
170 { CP_ORG, TS, "org" }, /* 18 */
171 { CP_REC, TS, "rec" }, /* 19 */
172 { CP_XMT, TS, "xmt" }, /* 20 */
173 { CP_REACH, OC, "reach" }, /* 21 */
174 { CP_UNREACH, UI, "unreach" }, /* 22 */
175 { CP_TIMER, UI, "timer" }, /* 23 */
176 { CP_DELAY, FS, "delay" }, /* 24 */
177 { CP_OFFSET, FL, "offset" }, /* 25 */
178 { CP_JITTER, FU, "jitter" }, /* 26 */
179 { CP_DISPERSION, FU, "dispersion" }, /* 27 */
180 { CP_KEYID, UI, "keyid" }, /* 28 */
181 { CP_FILTDELAY, AR, "filtdelay" }, /* 29 */
182 { CP_FILTOFFSET, AR, "filtoffset" }, /* 30 */
183 { CP_PMODE, ST, "pmode" }, /* 31 */
184 { CP_RECEIVED, UI, "received" }, /* 32 */
185 { CP_SENT, UI, "sent" }, /* 33 */
186 { CP_FILTERROR, AR, "filtdisp" }, /* 34 */
187 { CP_FLASH, FX, "flash" }, /* 35 */
188 { CP_TTL, UI, "ttl" }, /* 36 */
190 * These are duplicate entries so that we can
191 * process deviant version of the ntp protocol.
193 { CP_SRCADR, HA, "peeraddr" }, /* 4 */
194 { CP_SRCPORT, UI, "peerport" }, /* 5 */
195 { CP_PPOLL, UI, "peerpoll" }, /* 11 */
196 { CP_HPOLL, UI, "hostpoll" }, /* 12 */
197 { CP_FILTERROR, AR, "filterror" }, /* 34 */
198 { 0, EOV, "" }
203 * Clock variable list
205 struct ctl_var clock_var[] = {
206 { 0, PADDING, "" }, /* 0 */
207 { CC_TYPE, UI, "type" }, /* 1 */
208 { CC_TIMECODE, ST, "timecode" }, /* 2 */
209 { CC_POLL, UI, "poll" }, /* 3 */
210 { CC_NOREPLY, UI, "noreply" }, /* 4 */
211 { CC_BADFORMAT, UI, "badformat" }, /* 5 */
212 { CC_BADDATA, UI, "baddata" }, /* 6 */
213 { CC_FUDGETIME1, FL, "fudgetime1" }, /* 7 */
214 { CC_FUDGETIME2, FL, "fudgetime2" }, /* 8 */
215 { CC_FUDGEVAL1, UI, "stratum" }, /* 9 */
216 { CC_FUDGEVAL2, RF, "refid" }, /* 10 */
217 { CC_FLAGS, UI, "flags" }, /* 11 */
218 { CC_DEVICE, ST, "device" }, /* 12 */
219 { 0, EOV, "" }
224 * flasher bits
226 static const char *tstflagnames[] = {
227 "pkt_dup", /* TEST1 */
228 "pkt_bogus", /* TEST2 */
229 "pkt_proto", /* TEST3 */
230 "pkt_denied", /* TEST4 */
231 "pkt_auth", /* TEST5 */
232 "pkt_synch", /* TEST6 */
233 "pkt_dist", /* TEST7 */
234 "pkt_autokey", /* TEST8 */
235 "pkt_crypto", /* TEST9 */
236 "peer_stratum", /* TEST10 */
237 "peer_dist", /* TEST11 */
238 "peer_loop", /* TEST12 */
239 "peer_unfit" /* TEST13 */
243 int ntpqmain P((int, char **));
245 * Built in command handler declarations
247 static int openhost P((const char *));
248 static int sendpkt P((char *, int));
249 static int getresponse P((int, int, u_short *, int *, char **, int));
250 static int sendrequest P((int, int, int, int, char *));
251 static char * tstflags P((u_long));
252 static void getcmds P((void));
253 static RETSIGTYPE abortcmd P((int));
254 static void docmd P((const char *));
255 static void tokenize P((const char *, char **, int *));
256 static int findcmd P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
257 static int getarg P((char *, int, arg_v *));
258 static int rtdatetolfp P((char *, l_fp *));
259 static int decodearr P((char *, int *, l_fp *));
260 static void help P((struct parse *, FILE *));
261 #ifdef QSORT_USES_VOID_P
262 static int helpsort P((const void *, const void *));
263 #else
264 static int helpsort P((char **, char **));
265 #endif
266 static void printusage P((struct xcmd *, FILE *));
267 static void timeout P((struct parse *, FILE *));
268 static void auth_delay P((struct parse *, FILE *));
269 static void host P((struct parse *, FILE *));
270 static void ntp_poll P((struct parse *, FILE *));
271 static void keyid P((struct parse *, FILE *));
272 static void keytype P((struct parse *, FILE *));
273 static void passwd P((struct parse *, FILE *));
274 static void hostnames P((struct parse *, FILE *));
275 static void setdebug P((struct parse *, FILE *));
276 static void quit P((struct parse *, FILE *));
277 static void version P((struct parse *, FILE *));
278 static void raw P((struct parse *, FILE *));
279 static void cooked P((struct parse *, FILE *));
280 static void authenticate P((struct parse *, FILE *));
281 static void ntpversion P((struct parse *, FILE *));
282 static void warning P((const char *, const char *, const char *));
283 static void error P((const char *, const char *, const char *));
284 static u_long getkeyid P((const char *));
285 static void atoascii P((int, char *, char *));
286 static void makeascii P((int, char *, FILE *));
287 static void rawprint P((int, int, char *, int, FILE *));
288 static void startoutput P((void));
289 static void output P((FILE *, char *, char *));
290 static void endoutput P((FILE *));
291 static void outputarr P((FILE *, char *, int, l_fp *));
292 static void cookedprint P((int, int, char *, int, FILE *));
293 #ifdef QSORT_USES_VOID_P
294 static int assoccmp P((const void *, const void *));
295 #else
296 static int assoccmp P((struct association *, struct association *));
297 #endif /* sgi || bsdi */
301 * Built-in commands we understand
303 struct xcmd builtins[] = {
304 { "?", help, { OPT|NTP_STR, NO, NO, NO },
305 { "command", "", "", "" },
306 "tell the use and syntax of commands" },
307 { "help", help, { OPT|NTP_STR, NO, NO, NO },
308 { "command", "", "", "" },
309 "tell the use and syntax of commands" },
310 { "timeout", timeout, { OPT|NTP_UINT, NO, NO, NO },
311 { "msec", "", "", "" },
312 "set the primary receive time out" },
313 { "delay", auth_delay, { OPT|NTP_INT, NO, NO, NO },
314 { "msec", "", "", "" },
315 "set the delay added to encryption time stamps" },
316 { "host", host, { OPT|NTP_STR, OPT|NTP_STR, NO, NO },
317 { "-4|-6", "hostname", "", "" },
318 "specify the host whose NTP server we talk to" },
319 { "poll", ntp_poll, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
320 { "n", "verbose", "", "" },
321 "poll an NTP server in client mode `n' times" },
322 { "passwd", passwd, { NO, NO, NO, NO },
323 { "", "", "", "" },
324 "specify a password to use for authenticated requests"},
325 { "hostnames", hostnames, { OPT|NTP_STR, NO, NO, NO },
326 { "yes|no", "", "", "" },
327 "specify whether hostnames or net numbers are printed"},
328 { "debug", setdebug, { OPT|NTP_STR, NO, NO, NO },
329 { "no|more|less", "", "", "" },
330 "set/change debugging level" },
331 { "quit", quit, { NO, NO, NO, NO },
332 { "", "", "", "" },
333 "exit ntpq" },
334 { "exit", quit, { NO, NO, NO, NO },
335 { "", "", "", "" },
336 "exit ntpq" },
337 { "keyid", keyid, { OPT|NTP_UINT, NO, NO, NO },
338 { "key#", "", "", "" },
339 "set keyid to use for authenticated requests" },
340 { "version", version, { NO, NO, NO, NO },
341 { "", "", "", "" },
342 "print version number" },
343 { "raw", raw, { NO, NO, NO, NO },
344 { "", "", "", "" },
345 "do raw mode variable output" },
346 { "cooked", cooked, { NO, NO, NO, NO },
347 { "", "", "", "" },
348 "do cooked mode variable output" },
349 { "authenticate", authenticate, { OPT|NTP_STR, NO, NO, NO },
350 { "yes|no", "", "", "" },
351 "always authenticate requests to this server" },
352 { "ntpversion", ntpversion, { OPT|NTP_UINT, NO, NO, NO },
353 { "version number", "", "", "" },
354 "set the NTP version number to use for requests" },
355 { "keytype", keytype, { OPT|NTP_STR, NO, NO, NO },
356 { "key type (md5|des)", "", "", "" },
357 "set key type to use for authenticated requests (des|md5)" },
358 { 0, 0, { NO, NO, NO, NO },
359 { "", "", "", "" }, "" }
364 * Default values we use.
366 #define DEFTIMEOUT (5) /* 5 second time out */
367 #define DEFSTIMEOUT (2) /* 2 second time out after first */
368 #define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
369 #define DEFHOST "localhost" /* default host name */
370 #define LENHOSTNAME 256 /* host name is 256 characters long */
371 #define MAXCMDS 100 /* maximum commands on cmd line */
372 #define MAXHOSTS 200 /* maximum hosts on cmd line */
373 #define MAXLINE 512 /* maximum line length */
374 #define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */
375 #define MAXVARLEN 256 /* maximum length of a variable name */
376 #define MAXVALLEN 400 /* maximum length of a variable value */
377 #define MAXOUTLINE 72 /* maximum length of an output line */
378 #define SCREENWIDTH 76 /* nominal screen width in columns */
381 * Some variables used and manipulated locally
383 struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */
384 struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */
385 l_fp delay_time; /* delay time */
386 char currenthost[LENHOSTNAME]; /* current host name */
387 struct sockaddr_in hostaddr = { 0 }; /* host address */
388 int showhostnames = 1; /* show host names by default */
390 int ai_fam_templ; /* address family */
391 int ai_fam_default; /* default address family */
392 SOCKET sockfd; /* fd socket is opened on */
393 int havehost = 0; /* set to 1 when host open */
394 int s_port = 0;
395 struct servent *server_entry = NULL; /* server entry for ntp */
397 #ifdef SYS_WINNT
398 DWORD NumberOfBytesWritten;
400 HANDLE TimerThreadHandle = NULL; /* 1998/06/03 - Used in ntplib/machines.c */
401 void timer(void) { ; }; /* 1998/06/03 - Used in ntplib/machines.c */
403 #endif /* SYS_WINNT */
406 * Sequence number used for requests. It is incremented before
407 * it is used.
409 u_short sequence;
412 * Holds data returned from queries. Declare buffer long to be sure of
413 * alignment.
415 #define MAXFRAGS 24 /* maximum number of fragments */
416 #define DATASIZE (MAXFRAGS*480) /* maximum amount of data */
417 long pktdata[DATASIZE/sizeof(long)];
420 * Holds association data for use with the &n operator.
422 struct association assoc_cache[MAXASSOC];
423 int numassoc = 0; /* number of cached associations */
426 * For commands typed on the command line (with the -c option)
428 int numcmds = 0;
429 const char *ccmds[MAXCMDS];
430 #define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
433 * When multiple hosts are specified.
435 int numhosts = 0;
436 const char *chosts[MAXHOSTS];
437 #define ADDHOST(cp) if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
440 * Error codes for internal use
442 #define ERR_UNSPEC 256
443 #define ERR_INCOMPLETE 257
444 #define ERR_TIMEOUT 258
445 #define ERR_TOOMUCH 259
448 * Macro definitions we use
450 #define ISSPACE(c) ((c) == ' ' || (c) == '\t')
451 #define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0')
452 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
455 * Jump buffer for longjumping back to the command level
457 jmp_buf interrupt_buf;
460 * Points at file being currently printed into
462 FILE *current_output;
465 * Command table imported from ntpdc_ops.c
467 extern struct xcmd opcmds[];
469 char *progname;
470 volatile int debug;
472 #ifdef NO_MAIN_ALLOWED
473 CALL(ntpq,"ntpq",ntpqmain);
475 void clear_globals(void)
477 extern int ntp_optind;
478 showhostnames = 0; /* don'tshow host names by default */
479 ntp_optind = 0;
480 server_entry = NULL; /* server entry for ntp */
481 havehost = 0; /* set to 1 when host open */
482 numassoc = 0; /* number of cached associations */
483 numcmds = 0;
484 numhosts = 0;
486 #endif
489 * main - parse arguments and handle options
491 #ifndef NO_MAIN_ALLOWED
493 main(
494 int argc,
495 char *argv[]
498 return ntpqmain(argc, argv);
500 #endif
503 ntpqmain(
504 int argc,
505 char *argv[]
508 extern int ntp_optind;
510 #ifdef SYS_VXWORKS
511 clear_globals();
512 taskPrioritySet(taskIdSelf(), 100 );
513 #endif
515 delay_time.l_ui = 0;
516 delay_time.l_uf = DEFDELAY;
518 #ifdef SYS_WINNT
519 if (!Win32InitSockets())
521 fprintf(stderr, "No useable winsock.dll:");
522 exit(1);
524 #endif /* SYS_WINNT */
526 /* Check to see if we have IPv6. Otherwise force the -4 flag */
527 if (isc_net_probeipv6() != ISC_R_SUCCESS) {
528 ai_fam_default = AF_INET;
531 progname = argv[0];
534 int optct = optionProcess(&ntpqOptions, argc, argv);
535 argc -= optct;
536 argv += optct;
539 switch (WHICH_IDX_IPV4) {
540 case INDEX_OPT_IPV4:
541 ai_fam_templ = AF_INET;
542 break;
543 case INDEX_OPT_IPV6:
544 ai_fam_templ = AF_INET6;
545 break;
546 default:
547 ai_fam_templ = ai_fam_default;
548 break;
551 if (HAVE_OPT(COMMAND)) {
552 int cmdct = STACKCT_OPT( COMMAND );
553 const char** cmds = STACKLST_OPT( COMMAND );
555 while (cmdct-- > 0) {
556 ADDCMD(*cmds++);
560 debug = DESC(DEBUG_LEVEL).optOccCt;
562 if (HAVE_OPT(INTERACTIVE)) {
563 interactive = 1;
566 if (HAVE_OPT(NUMERIC)) {
567 showhostnames = 0;
570 if (HAVE_OPT(PEERS)) {
571 ADDCMD("peers");
574 #if 0
575 while ((c = ntp_getopt(argc, argv, "46c:dinp")) != EOF)
576 switch (c) {
577 case '4':
578 ai_fam_templ = AF_INET;
579 break;
580 case '6':
581 ai_fam_templ = AF_INET6;
582 break;
583 case 'c':
584 ADDCMD(ntp_optarg);
585 break;
586 case 'd':
587 ++debug;
588 break;
589 case 'i':
590 interactive = 1;
591 break;
592 case 'n':
593 showhostnames = 0;
594 break;
595 case 'p':
596 ADDCMD("peers");
597 break;
598 default:
599 errflg++;
600 break;
602 if (errflg) {
603 (void) fprintf(stderr,
604 "usage: %s [-46dinp] [-c cmd] host ...\n",
605 progname);
606 exit(2);
608 #endif
609 if (ntp_optind == argc) {
610 ADDHOST(DEFHOST);
611 } else {
612 for (; ntp_optind < argc; ntp_optind++)
613 ADDHOST(argv[ntp_optind]);
616 if (numcmds == 0 && interactive == 0
617 && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
618 interactive = 1;
621 #ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
622 if (interactive)
623 (void) signal_no_reset(SIGINT, abortcmd);
624 #endif /* SYS_WINNT */
626 if (numcmds == 0) {
627 (void) openhost(chosts[0]);
628 getcmds();
629 } else {
630 int ihost;
631 int icmd;
633 for (ihost = 0; ihost < numhosts; ihost++) {
634 if (openhost(chosts[ihost]))
635 for (icmd = 0; icmd < numcmds; icmd++)
636 docmd(ccmds[icmd]);
639 #ifdef SYS_WINNT
640 WSACleanup();
641 #endif /* SYS_WINNT */
642 return 0;
647 * openhost - open a socket to a host
649 static int
650 openhost(
651 const char *hname
654 char temphost[LENHOSTNAME];
655 int a_info, i;
656 struct addrinfo hints, *ai = NULL;
657 register const char *cp;
658 char name[LENHOSTNAME];
659 char service[5];
662 * We need to get by the [] if they were entered
665 cp = hname;
667 if(*cp == '[') {
668 cp++;
669 for(i = 0; *cp != ']'; cp++, i++)
670 name[i] = *cp;
671 name[i] = '\0';
672 hname = name;
676 * First try to resolve it as an ip address and if that fails,
677 * do a fullblown (dns) lookup. That way we only use the dns
678 * when it is needed and work around some implementations that
679 * will return an "IPv4-mapped IPv6 address" address if you
680 * give it an IPv4 address to lookup.
682 strcpy(service, "ntp");
683 memset((char *)&hints, 0, sizeof(struct addrinfo));
684 hints.ai_family = ai_fam_templ;
685 hints.ai_protocol = IPPROTO_UDP;
686 hints.ai_socktype = SOCK_DGRAM;
687 hints.ai_flags = AI_NUMERICHOST;
689 a_info = getaddrinfo(hname, service, &hints, &ai);
690 if (a_info == EAI_NONAME
691 #ifdef EAI_NODATA
692 || a_info == EAI_NODATA
693 #endif
695 hints.ai_flags = AI_CANONNAME;
696 #ifdef AI_ADDRCONFIG
697 hints.ai_flags |= AI_ADDRCONFIG;
698 #endif
699 a_info = getaddrinfo(hname, service, &hints, &ai);
701 /* Some older implementations don't like AI_ADDRCONFIG. */
702 if (a_info == EAI_BADFLAGS) {
703 hints.ai_flags = AI_CANONNAME;
704 a_info = getaddrinfo(hname, service, &hints, &ai);
706 if (a_info != 0) {
707 (void) fprintf(stderr, "%s\n", gai_strerror(a_info));
708 return 0;
711 if (ai->ai_canonname == NULL) {
712 strncpy(temphost, stoa((struct sockaddr_storage *)ai->ai_addr),
713 LENHOSTNAME);
714 temphost[LENHOSTNAME-1] = '\0';
716 } else {
717 strncpy(temphost, ai->ai_canonname, LENHOSTNAME);
718 temphost[LENHOSTNAME-1] = '\0';
721 if (debug > 2)
722 printf("Opening host %s\n", temphost);
724 if (havehost == 1) {
725 if (debug > 2)
726 printf("Closing old host %s\n", currenthost);
727 (void) closesocket(sockfd);
728 havehost = 0;
730 (void) strcpy(currenthost, temphost);
732 /* port maps to the same location in both families */
733 s_port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port;
734 #ifdef SYS_VXWORKS
735 ((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
736 if (ai->ai_family == AF_INET)
737 *(struct sockaddr_in *)&hostaddr=
738 *((struct sockaddr_in *)ai->ai_addr);
739 else
740 *(struct sockaddr_in6 *)&hostaddr=
741 *((struct sockaddr_in6 *)ai->ai_addr);
742 #endif /* SYS_VXWORKS */
744 #ifdef SYS_WINNT
746 int optionValue = SO_SYNCHRONOUS_NONALERT;
747 int err;
749 err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue));
750 if (err != NO_ERROR) {
751 (void) fprintf(stderr, "cannot open nonoverlapped sockets\n");
752 exit(1);
755 #endif /* SYS_WINNT */
757 sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
758 if (sockfd == INVALID_SOCKET) {
759 error("socket", "", "");
763 #ifdef NEED_RCVBUF_SLOP
764 # ifdef SO_RCVBUF
765 { int rbufsize = DATASIZE + 2048; /* 2K for slop */
766 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
767 &rbufsize, sizeof(int)) == -1)
768 error("setsockopt", "", "");
770 # endif
771 #endif
773 #ifdef SYS_VXWORKS
774 if (connect(sockfd, (struct sockaddr *)&hostaddr,
775 sizeof(hostaddr)) == -1)
776 #else
777 if (connect(sockfd, (struct sockaddr *)ai->ai_addr,
778 ai->ai_addrlen) == -1)
779 #endif /* SYS_VXWORKS */
780 error("connect", "", "");
781 if (a_info == 0)
782 freeaddrinfo(ai);
783 havehost = 1;
784 return 1;
788 /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
790 * sendpkt - send a packet to the remote host
792 static int
793 sendpkt(
794 char *xdata,
795 int xdatalen
798 if (debug >= 3)
799 printf("Sending %d octets\n", xdatalen);
802 if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) {
803 warning("write to %s failed", currenthost, "");
804 return -1;
807 if (debug >= 4) {
808 int first = 8;
809 printf("Packet data:\n");
810 while (xdatalen-- > 0) {
811 if (first-- == 0) {
812 printf("\n");
813 first = 7;
815 printf(" %02x", *xdata++ & 0xff);
817 printf("\n");
819 return 0;
825 * getresponse - get a (series of) response packet(s) and return the data
827 static int
828 getresponse(
829 int opcode,
830 int associd,
831 u_short *rstatus,
832 int *rsize,
833 char **rdata,
834 int timeo
837 struct ntp_control rpkt;
838 struct timeval tvo;
839 u_short offsets[MAXFRAGS+1];
840 u_short counts[MAXFRAGS+1];
841 u_short offset;
842 u_short count;
843 int numfrags;
844 int seenlastfrag;
845 fd_set fds;
846 int n;
849 * This is pretty tricky. We may get between 1 and MAXFRAG packets
850 * back in response to the request. We peel the data out of
851 * each packet and collect it in one long block. When the last
852 * packet in the sequence is received we'll know how much data we
853 * should have had. Note we use one long time out, should reconsider.
855 *rsize = 0;
856 if (rstatus)
857 *rstatus = 0;
858 *rdata = (char *)pktdata;
860 numfrags = 0;
861 seenlastfrag = 0;
863 FD_ZERO(&fds);
865 again:
866 if (numfrags == 0)
867 tvo = tvout;
868 else
869 tvo = tvsout;
871 FD_SET(sockfd, &fds);
872 n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
874 #if 0
875 if (debug >= 1)
876 printf("select() returns %d\n", n);
877 #endif
879 if (n == -1) {
880 warning("select fails", "", "");
881 return -1;
883 if (n == 0) {
885 * Timed out. Return what we have
887 if (numfrags == 0) {
888 if (timeo)
889 (void) fprintf(stderr,
890 "%s: timed out, nothing received\n",
891 currenthost);
892 return ERR_TIMEOUT;
893 } else {
894 if (timeo)
895 (void) fprintf(stderr,
896 "%s: timed out with incomplete data\n",
897 currenthost);
898 if (debug) {
899 printf("Received fragments:\n");
900 for (n = 0; n < numfrags; n++)
901 printf("%4d %d\n", offsets[n],
902 counts[n]);
903 if (seenlastfrag)
904 printf("last fragment received\n");
905 else
906 printf("last fragment not received\n");
908 return ERR_INCOMPLETE;
912 n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
913 if (n == -1) {
914 warning("read", "", "");
915 return -1;
918 if (debug >= 4) {
919 int len = n, first = 8;
920 char *data = (char *)&rpkt;
922 printf("Packet data:\n");
923 while (len-- > 0) {
924 if (first-- == 0) {
925 printf("\n");
926 first = 7;
928 printf(" %02x", *data++ & 0xff);
930 printf("\n");
934 * Check for format errors. Bug proofing.
936 if (n < CTL_HEADER_LEN) {
937 if (debug)
938 printf("Short (%d byte) packet received\n", n);
939 goto again;
941 if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
942 || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
943 if (debug)
944 printf("Packet received with version %d\n",
945 PKT_VERSION(rpkt.li_vn_mode));
946 goto again;
948 if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
949 if (debug)
950 printf("Packet received with mode %d\n",
951 PKT_MODE(rpkt.li_vn_mode));
952 goto again;
954 if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
955 if (debug)
956 printf("Received request packet, wanted response\n");
957 goto again;
961 * Check opcode and sequence number for a match.
962 * Could be old data getting to us.
964 if (ntohs(rpkt.sequence) != sequence) {
965 if (debug)
966 printf(
967 "Received sequnce number %d, wanted %d\n",
968 ntohs(rpkt.sequence), sequence);
969 goto again;
971 if (CTL_OP(rpkt.r_m_e_op) != opcode) {
972 if (debug)
973 printf(
974 "Received opcode %d, wanted %d (sequence number okay)\n",
975 CTL_OP(rpkt.r_m_e_op), opcode);
976 goto again;
980 * Check the error code. If non-zero, return it.
982 if (CTL_ISERROR(rpkt.r_m_e_op)) {
983 int errcode;
985 errcode = (ntohs(rpkt.status) >> 8) & 0xff;
986 if (debug && CTL_ISMORE(rpkt.r_m_e_op)) {
987 printf("Error code %d received on not-final packet\n",
988 errcode);
990 if (errcode == CERR_UNSPEC)
991 return ERR_UNSPEC;
992 return errcode;
996 * Check the association ID to make sure it matches what
997 * we sent.
999 if (ntohs(rpkt.associd) != associd) {
1000 if (debug)
1001 printf("Association ID %d doesn't match expected %d\n",
1002 ntohs(rpkt.associd), associd);
1004 * Hack for silly fuzzballs which, at the time of writing,
1005 * return an assID of sys.peer when queried for system variables.
1007 #ifdef notdef
1008 goto again;
1009 #endif
1013 * Collect offset and count. Make sure they make sense.
1015 offset = ntohs(rpkt.offset);
1016 count = ntohs(rpkt.count);
1018 if (debug >= 3) {
1019 int shouldbesize;
1020 u_long key;
1021 u_long *lpkt;
1022 int maclen;
1025 * Usually we ignore authentication, but for debugging purposes
1026 * we watch it here.
1028 shouldbesize = CTL_HEADER_LEN + count;
1030 /* round to 8 octet boundary */
1031 shouldbesize = (shouldbesize + 7) & ~7;
1033 if (n & 0x3) {
1034 printf("Packet not padded, size = %d\n", n);
1035 } if ((maclen = n - shouldbesize) >= MIN_MAC_LEN) {
1036 printf(
1037 "Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1038 n, shouldbesize, maclen);
1039 lpkt = (u_long *)&rpkt;
1040 printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1041 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 3]),
1042 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 2]),
1043 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 1]),
1044 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long)]),
1045 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 1]),
1046 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 2]));
1047 key = ntohl(lpkt[(n - maclen) / sizeof(u_long)]);
1048 printf("Authenticated with keyid %lu\n", (u_long)key);
1049 if (key != 0 && key != info_auth_keyid) {
1050 printf("We don't know that key\n");
1051 } else {
1052 if (authdecrypt(key, (u_int32 *)&rpkt,
1053 n - maclen, maclen)) {
1054 printf("Auth okay!\n");
1055 } else {
1056 printf("Auth failed!\n");
1062 if (debug >= 2)
1063 printf("Got packet, size = %d\n", n);
1064 if (count > (u_short)(n-CTL_HEADER_LEN)) {
1065 if (debug)
1066 printf(
1067 "Received count of %d octets, data in packet is %d\n",
1068 count, n-CTL_HEADER_LEN);
1069 goto again;
1071 if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1072 if (debug)
1073 printf("Received count of 0 in non-final fragment\n");
1074 goto again;
1076 if (offset + count > sizeof(pktdata)) {
1077 if (debug)
1078 printf("Offset %d, count %d, too big for buffer\n",
1079 offset, count);
1080 return ERR_TOOMUCH;
1082 if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1083 if (debug)
1084 printf("Received second last fragment packet\n");
1085 goto again;
1089 * So far, so good. Record this fragment, making sure it doesn't
1090 * overlap anything.
1092 if (debug >= 2)
1093 printf("Packet okay\n");;
1095 if (numfrags == MAXFRAGS) {
1096 if (debug)
1097 printf("Number of fragments exceeds maximum\n");
1098 return ERR_TOOMUCH;
1101 for (n = 0; n < numfrags; n++) {
1102 if (offset == offsets[n])
1103 goto again; /* duplicate */
1104 if (offset < offsets[n])
1105 break;
1108 if ((u_short)(n > 0 && offsets[n-1] + counts[n-1]) > offset)
1109 goto overlap;
1110 if (n < numfrags && (u_short)(offset + count) > offsets[n])
1111 goto overlap;
1114 register int i;
1116 for (i = numfrags; i > n; i--) {
1117 offsets[i] = offsets[i-1];
1118 counts[i] = counts[i-1];
1121 offsets[n] = offset;
1122 counts[n] = count;
1123 numfrags++;
1126 * Got that stuffed in right. Figure out if this was the last.
1127 * Record status info out of the last packet.
1129 if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1130 seenlastfrag = 1;
1131 if (rstatus != 0)
1132 *rstatus = ntohs(rpkt.status);
1136 * Copy the data into the data buffer.
1138 memmove((char *)pktdata + offset, (char *)rpkt.data, count);
1141 * If we've seen the last fragment, look for holes in the sequence.
1142 * If there aren't any, we're done.
1144 if (seenlastfrag && offsets[0] == 0) {
1145 for (n = 1; n < numfrags; n++) {
1146 if (offsets[n-1] + counts[n-1] != offsets[n])
1147 break;
1149 if (n == numfrags) {
1150 *rsize = offsets[numfrags-1] + counts[numfrags-1];
1151 return 0;
1154 goto again;
1156 overlap:
1158 * Print debugging message about overlapping fragments
1160 if (debug)
1161 printf("Overlapping fragments returned in response\n");
1162 goto again;
1167 * sendrequest - format and send a request packet
1169 static int
1170 sendrequest(
1171 int opcode,
1172 int associd,
1173 int auth,
1174 int qsize,
1175 char *qdata
1178 struct ntp_control qpkt;
1179 int pktsize;
1182 * Check to make sure the data will fit in one packet
1184 if (qsize > CTL_MAX_DATA_LEN) {
1185 (void) fprintf(stderr,
1186 "***Internal error! qsize (%d) too large\n",
1187 qsize);
1188 return 1;
1192 * Fill in the packet
1194 qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1195 qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
1196 qpkt.sequence = htons(sequence);
1197 qpkt.status = 0;
1198 qpkt.associd = htons((u_short)associd);
1199 qpkt.offset = 0;
1200 qpkt.count = htons((u_short)qsize);
1203 * If we have data, copy it in and pad it out to a 64
1204 * bit boundary.
1206 if (qsize > 0) {
1207 memmove((char *)qpkt.data, qdata, (unsigned)qsize);
1208 pktsize = qsize + CTL_HEADER_LEN;
1209 while (pktsize & (sizeof(u_long) - 1)) {
1210 qpkt.data[qsize++] = 0;
1211 pktsize++;
1213 } else {
1214 pktsize = CTL_HEADER_LEN;
1218 * If it isn't authenticated we can just send it. Otherwise
1219 * we're going to have to think about it a little.
1221 if (!auth && !always_auth) {
1222 return sendpkt((char *)&qpkt, pktsize);
1223 } else {
1224 const char *pass = "\0";
1225 int maclen = 0;
1226 u_long my_keyid;
1229 * Pad out packet to a multiple of 8 octets to be sure
1230 * receiver can handle it.
1232 while (pktsize & 7) {
1233 qpkt.data[qsize++] = 0;
1234 pktsize++;
1238 * Get the keyid and the password if we don't have one.
1240 if (info_auth_keyid == 0) {
1241 int u_keyid = getkeyid("Keyid: ");
1242 if (u_keyid == 0 || u_keyid > NTP_MAXKEY) {
1243 (void) fprintf(stderr,
1244 "Invalid key identifier\n");
1245 return 1;
1247 info_auth_keyid = u_keyid;
1249 if (!authistrusted(info_auth_keyid)) {
1250 pass = getpass("MD5 Password: ");
1251 if (*pass == '\0') {
1252 (void) fprintf(stderr,
1253 "Invalid password\n");
1254 return (1);
1257 authusekey(info_auth_keyid, info_auth_keytype, (const u_char *)pass);
1258 authtrust(info_auth_keyid, 1);
1261 * Stick the keyid in the packet where
1262 * cp currently points. Cp should be aligned
1263 * properly. Then do the encryptions.
1265 my_keyid = htonl(info_auth_keyid);
1266 memcpy(&qpkt.data[qsize], &my_keyid, sizeof my_keyid);
1267 maclen = authencrypt(info_auth_keyid, (u_int32 *)&qpkt,
1268 pktsize);
1269 if (maclen == 0) {
1270 (void) fprintf(stderr, "Key not found\n");
1271 return (1);
1273 return sendpkt((char *)&qpkt, pktsize + maclen);
1275 /*NOTREACHED*/
1280 * doquery - send a request and process the response
1283 doquery(
1284 int opcode,
1285 int associd,
1286 int auth,
1287 int qsize,
1288 char *qdata,
1289 u_short *rstatus,
1290 int *rsize,
1291 char **rdata
1294 int res;
1295 int done;
1298 * Check to make sure host is open
1300 if (!havehost) {
1301 (void) fprintf(stderr, "***No host open, use `host' command\n");
1302 return -1;
1305 done = 0;
1306 sequence++;
1308 again:
1310 * send a request
1312 res = sendrequest(opcode, associd, auth, qsize, qdata);
1313 if (res != 0)
1314 return res;
1317 * Get the response. If we got a standard error, print a message
1319 res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1321 if (res > 0) {
1322 if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1323 if (res == ERR_INCOMPLETE) {
1325 * better bump the sequence so we don't
1326 * get confused about differing fragments.
1328 sequence++;
1330 done = 1;
1331 goto again;
1333 if (numhosts > 1)
1334 (void) fprintf(stderr, "server=%s ", currenthost);
1335 switch(res) {
1336 case CERR_BADFMT:
1337 (void) fprintf(stderr,
1338 "***Server reports a bad format request packet\n");
1339 break;
1340 case CERR_PERMISSION:
1341 (void) fprintf(stderr,
1342 "***Server disallowed request (authentication?)\n");
1343 break;
1344 case CERR_BADOP:
1345 (void) fprintf(stderr,
1346 "***Server reports a bad opcode in request\n");
1347 break;
1348 case CERR_BADASSOC:
1349 (void) fprintf(stderr,
1350 "***Association ID %d unknown to server\n",associd);
1351 break;
1352 case CERR_UNKNOWNVAR:
1353 (void) fprintf(stderr,
1354 "***A request variable unknown to the server\n");
1355 break;
1356 case CERR_BADVALUE:
1357 (void) fprintf(stderr,
1358 "***Server indicates a request variable was bad\n");
1359 break;
1360 case ERR_UNSPEC:
1361 (void) fprintf(stderr,
1362 "***Server returned an unspecified error\n");
1363 break;
1364 case ERR_TIMEOUT:
1365 (void) fprintf(stderr, "***Request timed out\n");
1366 break;
1367 case ERR_INCOMPLETE:
1368 (void) fprintf(stderr,
1369 "***Response from server was incomplete\n");
1370 break;
1371 case ERR_TOOMUCH:
1372 (void) fprintf(stderr,
1373 "***Buffer size exceeded for returned data\n");
1374 break;
1375 default:
1376 (void) fprintf(stderr,
1377 "***Server returns unknown error code %d\n", res);
1378 break;
1381 return res;
1386 * getcmds - read commands from the standard input and execute them
1388 static void
1389 getcmds(void)
1391 #if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
1392 char *line;
1394 for (;;) {
1395 if ((line = readline(interactive?prompt:"")) == NULL) return;
1396 if (*line) add_history(line);
1397 docmd(line);
1398 free(line);
1400 #else /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */
1401 char line[MAXLINE];
1403 for (;;) {
1404 if (interactive) {
1405 #ifdef VMS /* work around a problem with mixing stdout & stderr */
1406 fputs("",stdout);
1407 #endif
1408 (void) fputs(prompt, stderr);
1409 (void) fflush(stderr);
1412 if (fgets(line, sizeof line, stdin) == NULL)
1413 return;
1415 docmd(line);
1417 #endif /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */
1420 #ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
1422 * abortcmd - catch interrupts and abort the current command
1424 static RETSIGTYPE
1425 abortcmd(
1426 int sig
1429 if (current_output == stdout)
1430 (void) fflush(stdout);
1431 putc('\n', stderr);
1432 (void) fflush(stderr);
1433 if (jump) longjmp(interrupt_buf, 1);
1435 #endif /* SYS_WINNT */
1438 * docmd - decode the command line and execute a command
1440 static void
1441 docmd(
1442 const char *cmdline
1445 char *tokens[1+MAXARGS+2];
1446 struct parse pcmd;
1447 int ntok;
1448 static int i;
1449 struct xcmd *xcmd = NULL;
1452 * Tokenize the command line. If nothing on it, return.
1454 tokenize(cmdline, tokens, &ntok);
1455 if (ntok == 0)
1456 return;
1459 * Find the appropriate command description.
1461 i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1462 if (i == 0) {
1463 (void) fprintf(stderr, "***Command `%s' unknown\n",
1464 tokens[0]);
1465 return;
1466 } else if (i >= 2) {
1467 (void) fprintf(stderr, "***Command `%s' ambiguous\n",
1468 tokens[0]);
1469 return;
1473 * Save the keyword, then walk through the arguments, interpreting
1474 * as we go.
1476 pcmd.keyword = tokens[0];
1477 pcmd.nargs = 0;
1478 for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1479 if ((i+1) >= ntok) {
1480 if (!(xcmd->arg[i] & OPT)) {
1481 printusage(xcmd, stderr);
1482 return;
1484 break;
1486 if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1487 break;
1488 if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1489 return;
1490 pcmd.nargs++;
1493 i++;
1494 if (i < ntok && *tokens[i] == '>') {
1495 char *fname;
1497 if (*(tokens[i]+1) != '\0')
1498 fname = tokens[i]+1;
1499 else if ((i+1) < ntok)
1500 fname = tokens[i+1];
1501 else {
1502 (void) fprintf(stderr, "***No file for redirect\n");
1503 return;
1506 current_output = fopen(fname, "w");
1507 if (current_output == NULL) {
1508 (void) fprintf(stderr, "***Error opening %s: ", fname);
1509 perror("");
1510 return;
1512 i = 1; /* flag we need a close */
1513 } else {
1514 current_output = stdout;
1515 i = 0; /* flag no close */
1518 if (interactive && setjmp(interrupt_buf)) {
1519 jump = 0;
1520 return;
1521 } else {
1522 jump++;
1523 (xcmd->handler)(&pcmd, current_output);
1524 jump = 0; /* HMS: 961106: was after fclose() */
1525 if (i) (void) fclose(current_output);
1531 * tokenize - turn a command line into tokens
1533 static void
1534 tokenize(
1535 const char *line,
1536 char **tokens,
1537 int *ntok
1540 register const char *cp;
1541 register char *sp;
1542 static char tspace[MAXLINE];
1544 sp = tspace;
1545 cp = line;
1546 for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1547 tokens[*ntok] = sp;
1548 while (ISSPACE(*cp))
1549 cp++;
1550 if (ISEOL(*cp))
1551 break;
1552 do {
1553 *sp++ = *cp++;
1554 } while (!ISSPACE(*cp) && !ISEOL(*cp));
1556 *sp++ = '\0';
1563 * findcmd - find a command in a command description table
1565 static int
1566 findcmd(
1567 register char *str,
1568 struct xcmd *clist1,
1569 struct xcmd *clist2,
1570 struct xcmd **cmd
1573 register struct xcmd *cl;
1574 register int clen;
1575 int nmatch;
1576 struct xcmd *nearmatch = NULL;
1577 struct xcmd *clist;
1579 clen = strlen(str);
1580 nmatch = 0;
1581 if (clist1 != 0)
1582 clist = clist1;
1583 else if (clist2 != 0)
1584 clist = clist2;
1585 else
1586 return 0;
1588 again:
1589 for (cl = clist; cl->keyword != 0; cl++) {
1590 /* do a first character check, for efficiency */
1591 if (*str != *(cl->keyword))
1592 continue;
1593 if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1595 * Could be extact match, could be approximate.
1596 * Is exact if the length of the keyword is the
1597 * same as the str.
1599 if (*((cl->keyword) + clen) == '\0') {
1600 *cmd = cl;
1601 return 1;
1603 nmatch++;
1604 nearmatch = cl;
1609 * See if there is more to do. If so, go again. Sorry about the
1610 * goto, too much looking at BSD sources...
1612 if (clist == clist1 && clist2 != 0) {
1613 clist = clist2;
1614 goto again;
1618 * If we got extactly 1 near match, use it, else return number
1619 * of matches.
1621 if (nmatch == 1) {
1622 *cmd = nearmatch;
1623 return 1;
1625 return nmatch;
1630 * getarg - interpret an argument token
1632 static int
1633 getarg(
1634 char *str,
1635 int code,
1636 arg_v *argp
1639 int isneg;
1640 char *cp, *np;
1641 static const char *digits = "0123456789";
1643 switch (code & ~OPT) {
1644 case NTP_STR:
1645 argp->string = str;
1646 break;
1647 case NTP_ADD:
1648 if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) {
1649 return 0;
1651 break;
1652 case NTP_INT:
1653 case NTP_UINT:
1654 isneg = 0;
1655 np = str;
1656 if (*np == '&') {
1657 np++;
1658 isneg = atoi(np);
1659 if (isneg <= 0) {
1660 (void) fprintf(stderr,
1661 "***Association value `%s' invalid/undecodable\n", str);
1662 return 0;
1664 if (isneg > numassoc) {
1665 if (numassoc == 0) {
1666 (void) fprintf(stderr,
1667 "***Association for `%s' unknown (max &%d)\n",
1668 str, numassoc);
1669 return 0;
1670 } else {
1671 isneg = numassoc;
1674 argp->uval = assoc_cache[isneg-1].assid;
1675 break;
1678 if (*np == '-') {
1679 np++;
1680 isneg = 1;
1683 argp->uval = 0;
1684 do {
1685 cp = strchr(digits, *np);
1686 if (cp == NULL) {
1687 (void) fprintf(stderr,
1688 "***Illegal integer value %s\n", str);
1689 return 0;
1691 argp->uval *= 10;
1692 argp->uval += (cp - digits);
1693 } while (*(++np) != '\0');
1695 if (isneg) {
1696 if ((code & ~OPT) == NTP_UINT) {
1697 (void) fprintf(stderr,
1698 "***Value %s should be unsigned\n", str);
1699 return 0;
1701 argp->ival = -argp->ival;
1703 break;
1704 case IP_VERSION:
1705 if (!strcmp("-6", str))
1706 argp->ival = 6 ;
1707 else if (!strcmp("-4", str))
1708 argp->ival = 4 ;
1709 else {
1710 (void) fprintf(stderr,
1711 "***Version must be either 4 or 6\n");
1712 return 0;
1714 break;
1717 return 1;
1722 * getnetnum - given a host name, return its net number
1723 * and (optional) full name
1726 getnetnum(
1727 const char *hname,
1728 struct sockaddr_storage *num,
1729 char *fullhost,
1730 int af
1733 int sockaddr_len;
1734 struct addrinfo hints, *ai = NULL;
1736 sockaddr_len = (af == AF_INET)
1737 ? sizeof(struct sockaddr_in)
1738 : sizeof(struct sockaddr_in6);
1739 memset((char *)&hints, 0, sizeof(struct addrinfo));
1740 hints.ai_flags = AI_CANONNAME;
1741 #ifdef AI_ADDRCONFIG
1742 hints.ai_flags |= AI_ADDRCONFIG;
1743 #endif
1745 /* decodenetnum works with addresses only */
1746 if (decodenetnum(hname, num)) {
1747 if (fullhost != 0) {
1748 getnameinfo((struct sockaddr *)num, sockaddr_len,
1749 fullhost, sizeof(fullhost), NULL, 0,
1750 NI_NUMERICHOST);
1752 return 1;
1753 } else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1754 memmove((char *)num, ai->ai_addr, ai->ai_addrlen);
1755 if (ai->ai_canonname != 0)
1756 (void) strcpy(fullhost, ai->ai_canonname);
1757 return 1;
1758 } else {
1759 (void) fprintf(stderr, "***Can't find host %s\n", hname);
1760 return 0;
1762 /*NOTREACHED*/
1766 * nntohost - convert network number to host name. This routine enforces
1767 * the showhostnames setting.
1769 char *
1770 nntohost(
1771 struct sockaddr_storage *netnum
1774 if (!showhostnames)
1775 return stoa(netnum);
1776 if ((netnum->ss_family == AF_INET) && ISREFCLOCKADR(netnum))
1777 return refnumtoa(netnum);
1778 return socktohost(netnum);
1783 * rtdatetolfp - decode an RT-11 date into an l_fp
1785 static int
1786 rtdatetolfp(
1787 char *str,
1788 l_fp *lfp
1791 register char *cp;
1792 register int i;
1793 struct calendar cal;
1794 char buf[4];
1795 static const char *months[12] = {
1796 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1797 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1800 cal.yearday = 0;
1803 * An RT-11 date looks like:
1805 * d[d]-Mth-y[y] hh:mm:ss
1807 * (No docs, but assume 4-digit years are also legal...)
1809 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
1811 cp = str;
1812 if (!isdigit((int)*cp)) {
1813 if (*cp == '-') {
1815 * Catch special case
1817 L_CLR(lfp);
1818 return 1;
1820 return 0;
1823 cal.monthday = (u_char) (*cp++ - '0'); /* ascii dependent */
1824 if (isdigit((int)*cp)) {
1825 cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
1826 cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
1829 if (*cp++ != '-')
1830 return 0;
1832 for (i = 0; i < 3; i++)
1833 buf[i] = *cp++;
1834 buf[3] = '\0';
1836 for (i = 0; i < 12; i++)
1837 if (STREQ(buf, months[i]))
1838 break;
1839 if (i == 12)
1840 return 0;
1841 cal.month = (u_char)(i + 1);
1843 if (*cp++ != '-')
1844 return 0;
1846 if (!isdigit((int)*cp))
1847 return 0;
1848 cal.year = (u_short)(*cp++ - '0');
1849 if (isdigit((int)*cp)) {
1850 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1851 cal.year = (u_short)(*cp++ - '0');
1853 if (isdigit((int)*cp)) {
1854 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1855 cal.year = (u_short)(cal.year + *cp++ - '0');
1857 if (isdigit((int)*cp)) {
1858 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1859 cal.year = (u_short)(cal.year + *cp++ - '0');
1863 * Catch special case. If cal.year == 0 this is a zero timestamp.
1865 if (cal.year == 0) {
1866 L_CLR(lfp);
1867 return 1;
1870 if (*cp++ != ' ' || !isdigit((int)*cp))
1871 return 0;
1872 cal.hour = (u_char)(*cp++ - '0');
1873 if (isdigit((int)*cp)) {
1874 cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
1875 cal.hour = (u_char)(cal.hour + *cp++ - '0');
1878 if (*cp++ != ':' || !isdigit((int)*cp))
1879 return 0;
1880 cal.minute = (u_char)(*cp++ - '0');
1881 if (isdigit((int)*cp)) {
1882 cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
1883 cal.minute = (u_char)(cal.minute + *cp++ - '0');
1886 if (*cp++ != ':' || !isdigit((int)*cp))
1887 return 0;
1888 cal.second = (u_char)(*cp++ - '0');
1889 if (isdigit((int)*cp)) {
1890 cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
1891 cal.second = (u_char)(cal.second + *cp++ - '0');
1895 * For RT-11, 1972 seems to be the pivot year
1897 if (cal.year < 72)
1898 cal.year += 2000;
1899 if (cal.year < 100)
1900 cal.year += 1900;
1902 lfp->l_ui = caltontp(&cal);
1903 lfp->l_uf = 0;
1904 return 1;
1909 * decodets - decode a timestamp into an l_fp format number, with
1910 * consideration of fuzzball formats.
1913 decodets(
1914 char *str,
1915 l_fp *lfp
1919 * If it starts with a 0x, decode as hex.
1921 if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
1922 return hextolfp(str+2, lfp);
1925 * If it starts with a '"', try it as an RT-11 date.
1927 if (*str == '"') {
1928 register char *cp = str+1;
1929 register char *bp;
1930 char buf[30];
1932 bp = buf;
1933 while (*cp != '"' && *cp != '\0' && bp < &buf[29])
1934 *bp++ = *cp++;
1935 *bp = '\0';
1936 return rtdatetolfp(buf, lfp);
1940 * Might still be hex. Check out the first character. Talk
1941 * about heuristics!
1943 if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
1944 return hextolfp(str, lfp);
1947 * Try it as a decimal. If this fails, try as an unquoted
1948 * RT-11 date. This code should go away eventually.
1950 if (atolfp(str, lfp))
1951 return 1;
1952 return rtdatetolfp(str, lfp);
1957 * decodetime - decode a time value. It should be in milliseconds
1960 decodetime(
1961 char *str,
1962 l_fp *lfp
1965 return mstolfp(str, lfp);
1970 * decodeint - decode an integer
1973 decodeint(
1974 char *str,
1975 long *val
1978 if (*str == '0') {
1979 if (*(str+1) == 'x' || *(str+1) == 'X')
1980 return hextoint(str+2, (u_long *)val);
1981 return octtoint(str, (u_long *)val);
1983 return atoint(str, val);
1988 * decodeuint - decode an unsigned integer
1991 decodeuint(
1992 char *str,
1993 u_long *val
1996 if (*str == '0') {
1997 if (*(str + 1) == 'x' || *(str + 1) == 'X')
1998 return (hextoint(str + 2, val));
1999 return (octtoint(str, val));
2001 return (atouint(str, val));
2006 * decodearr - decode an array of time values
2008 static int
2009 decodearr(
2010 char *str,
2011 int *narr,
2012 l_fp *lfparr
2015 register char *cp, *bp;
2016 register l_fp *lfp;
2017 char buf[60];
2019 lfp = lfparr;
2020 cp = str;
2021 *narr = 0;
2023 while (*narr < 8) {
2024 while (isspace((int)*cp))
2025 cp++;
2026 if (*cp == '\0')
2027 break;
2029 bp = buf;
2030 while (!isspace((int)*cp) && *cp != '\0')
2031 *bp++ = *cp++;
2032 *bp++ = '\0';
2034 if (!decodetime(buf, lfp))
2035 return 0;
2036 (*narr)++;
2037 lfp++;
2039 return 1;
2044 * Finally, the built in command handlers
2048 * help - tell about commands, or details of a particular command
2050 static void
2051 help(
2052 struct parse *pcmd,
2053 FILE *fp
2056 struct xcmd *xcp;
2057 char *cmd;
2058 const char *list[100];
2059 int word, words;
2060 int row, rows;
2061 int col, cols;
2063 if (pcmd->nargs == 0) {
2064 words = 0;
2065 for (xcp = builtins; xcp->keyword != 0; xcp++) {
2066 if (*(xcp->keyword) != '?')
2067 list[words++] = xcp->keyword;
2069 for (xcp = opcmds; xcp->keyword != 0; xcp++)
2070 list[words++] = xcp->keyword;
2072 qsort(
2073 #ifdef QSORT_USES_VOID_P
2074 (void *)
2075 #else
2076 (char *)
2077 #endif
2078 (list), (size_t)(words), sizeof(char *), helpsort);
2079 col = 0;
2080 for (word = 0; word < words; word++) {
2081 int length = strlen(list[word]);
2082 if (col < length) {
2083 col = length;
2087 cols = SCREENWIDTH / ++col;
2088 rows = (words + cols - 1) / cols;
2090 (void) fprintf(fp, "ntpq commands:\n");
2092 for (row = 0; row < rows; row++) {
2093 for (word = row; word < words; word += rows) {
2094 (void) fprintf(fp, "%-*.*s", col, col-1, list[word]);
2096 (void) fprintf(fp, "\n");
2098 } else {
2099 cmd = pcmd->argval[0].string;
2100 words = findcmd(cmd, builtins, opcmds, &xcp);
2101 if (words == 0) {
2102 (void) fprintf(stderr,
2103 "Command `%s' is unknown\n", cmd);
2104 return;
2105 } else if (words >= 2) {
2106 (void) fprintf(stderr,
2107 "Command `%s' is ambiguous\n", cmd);
2108 return;
2110 (void) fprintf(fp, "function: %s\n", xcp->comment);
2111 printusage(xcp, fp);
2117 * helpsort - do hostname qsort comparisons
2119 #ifdef QSORT_USES_VOID_P
2120 static int
2121 helpsort(
2122 const void *t1,
2123 const void *t2
2126 char const * const * name1 = (char const * const *)t1;
2127 char const * const * name2 = (char const * const *)t2;
2129 return strcmp(*name1, *name2);
2132 #else
2133 static int
2134 helpsort(
2135 char **name1,
2136 char **name2
2139 return strcmp(*name1, *name2);
2141 #endif
2144 * printusage - print usage information for a command
2146 static void
2147 printusage(
2148 struct xcmd *xcp,
2149 FILE *fp
2152 register int i;
2154 (void) fprintf(fp, "usage: %s", xcp->keyword);
2155 for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2156 if (xcp->arg[i] & OPT)
2157 (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2158 else
2159 (void) fprintf(fp, " %s", xcp->desc[i]);
2161 (void) fprintf(fp, "\n");
2166 * timeout - set time out time
2168 static void
2169 timeout(
2170 struct parse *pcmd,
2171 FILE *fp
2174 int val;
2176 if (pcmd->nargs == 0) {
2177 val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2178 (void) fprintf(fp, "primary timeout %d ms\n", val);
2179 } else {
2180 tvout.tv_sec = pcmd->argval[0].uval / 1000;
2181 tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
2182 * 1000;
2188 * auth_delay - set delay for auth requests
2190 static void
2191 auth_delay(
2192 struct parse *pcmd,
2193 FILE *fp
2196 int isneg;
2197 u_long val;
2199 if (pcmd->nargs == 0) {
2200 val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2201 (void) fprintf(fp, "delay %lu ms\n", val);
2202 } else {
2203 if (pcmd->argval[0].ival < 0) {
2204 isneg = 1;
2205 val = (u_long)(-pcmd->argval[0].ival);
2206 } else {
2207 isneg = 0;
2208 val = (u_long)pcmd->argval[0].ival;
2211 delay_time.l_ui = val / 1000;
2212 val %= 1000;
2213 delay_time.l_uf = val * 4294967; /* 2**32/1000 */
2215 if (isneg)
2216 L_NEG(&delay_time);
2222 * host - set the host we are dealing with.
2224 static void
2225 host(
2226 struct parse *pcmd,
2227 FILE *fp
2230 int i;
2232 if (pcmd->nargs == 0) {
2233 if (havehost)
2234 (void) fprintf(fp, "current host is %s\n", currenthost);
2235 else
2236 (void) fprintf(fp, "no current host\n");
2237 return;
2240 i = 0;
2241 ai_fam_templ = ai_fam_default;
2242 if (pcmd->nargs == 2) {
2243 if (!strcmp("-4", pcmd->argval[i].string))
2244 ai_fam_templ = AF_INET;
2245 else if (!strcmp("-6", pcmd->argval[i].string))
2246 ai_fam_templ = AF_INET6;
2247 else {
2248 if (havehost)
2249 (void) fprintf(fp,
2250 "current host remains %s\n", currenthost);
2251 else
2252 (void) fprintf(fp, "still no current host\n");
2253 return;
2255 i = 1;
2257 if (openhost(pcmd->argval[i].string)) {
2258 (void) fprintf(fp, "current host set to %s\n", currenthost);
2259 numassoc = 0;
2260 } else {
2261 if (havehost)
2262 (void) fprintf(fp,
2263 "current host remains %s\n", currenthost);
2264 else
2265 (void) fprintf(fp, "still no current host\n");
2271 * poll - do one (or more) polls of the host via NTP
2273 /*ARGSUSED*/
2274 static void
2275 ntp_poll(
2276 struct parse *pcmd,
2277 FILE *fp
2280 (void) fprintf(fp, "poll not implemented yet\n");
2285 * keyid - get a keyid to use for authenticating requests
2287 static void
2288 keyid(
2289 struct parse *pcmd,
2290 FILE *fp
2293 if (pcmd->nargs == 0) {
2294 if (info_auth_keyid == 0)
2295 (void) fprintf(fp, "no keyid defined\n");
2296 else
2297 (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
2298 } else {
2299 /* allow zero so that keyid can be cleared. */
2300 if(pcmd->argval[0].uval > NTP_MAXKEY)
2301 (void) fprintf(fp, "Invalid key identifier\n");
2302 info_auth_keyid = pcmd->argval[0].uval;
2307 * keytype - get type of key to use for authenticating requests
2309 static void
2310 keytype(
2311 struct parse *pcmd,
2312 FILE *fp
2315 if (pcmd->nargs == 0)
2316 fprintf(fp, "keytype is %s\n",
2317 (info_auth_keytype == KEY_TYPE_MD5) ? "MD5" : "???");
2318 else
2319 switch (*(pcmd->argval[0].string)) {
2320 case 'm':
2321 case 'M':
2322 info_auth_keytype = KEY_TYPE_MD5;
2323 break;
2325 default:
2326 fprintf(fp, "keytype must be 'md5'\n");
2333 * passwd - get an authentication key
2335 /*ARGSUSED*/
2336 static void
2337 passwd(
2338 struct parse *pcmd,
2339 FILE *fp
2342 char *pass;
2344 if (info_auth_keyid == 0) {
2345 int u_keyid = getkeyid("Keyid: ");
2346 if (u_keyid == 0 || u_keyid > NTP_MAXKEY) {
2347 (void)fprintf(fp, "Invalid key identifier\n");
2348 return;
2350 info_auth_keyid = u_keyid;
2352 pass = getpass("MD5 Password: ");
2353 if (*pass == '\0')
2354 (void) fprintf(fp, "Password unchanged\n");
2355 else {
2356 authusekey(info_auth_keyid, info_auth_keytype, (u_char *)pass);
2357 authtrust(info_auth_keyid, 1);
2363 * hostnames - set the showhostnames flag
2365 static void
2366 hostnames(
2367 struct parse *pcmd,
2368 FILE *fp
2371 if (pcmd->nargs == 0) {
2372 if (showhostnames)
2373 (void) fprintf(fp, "hostnames being shown\n");
2374 else
2375 (void) fprintf(fp, "hostnames not being shown\n");
2376 } else {
2377 if (STREQ(pcmd->argval[0].string, "yes"))
2378 showhostnames = 1;
2379 else if (STREQ(pcmd->argval[0].string, "no"))
2380 showhostnames = 0;
2381 else
2382 (void)fprintf(stderr, "What?\n");
2389 * setdebug - set/change debugging level
2391 static void
2392 setdebug(
2393 struct parse *pcmd,
2394 FILE *fp
2397 if (pcmd->nargs == 0) {
2398 (void) fprintf(fp, "debug level is %d\n", debug);
2399 return;
2400 } else if (STREQ(pcmd->argval[0].string, "no")) {
2401 debug = 0;
2402 } else if (STREQ(pcmd->argval[0].string, "more")) {
2403 debug++;
2404 } else if (STREQ(pcmd->argval[0].string, "less")) {
2405 debug--;
2406 } else {
2407 (void) fprintf(fp, "What?\n");
2408 return;
2410 (void) fprintf(fp, "debug level set to %d\n", debug);
2415 * quit - stop this nonsense
2417 /*ARGSUSED*/
2418 static void
2419 quit(
2420 struct parse *pcmd,
2421 FILE *fp
2424 if (havehost)
2425 closesocket(sockfd); /* cleanliness next to godliness */
2426 exit(0);
2431 * version - print the current version number
2433 /*ARGSUSED*/
2434 static void
2435 version(
2436 struct parse *pcmd,
2437 FILE *fp
2441 (void) fprintf(fp, "%s\n", Version);
2442 return;
2447 * raw - set raw mode output
2449 /*ARGSUSED*/
2450 static void
2451 raw(
2452 struct parse *pcmd,
2453 FILE *fp
2456 rawmode = 1;
2457 (void) fprintf(fp, "Output set to raw\n");
2462 * cooked - set cooked mode output
2464 /*ARGSUSED*/
2465 static void
2466 cooked(
2467 struct parse *pcmd,
2468 FILE *fp
2471 rawmode = 0;
2472 (void) fprintf(fp, "Output set to cooked\n");
2473 return;
2478 * authenticate - always authenticate requests to this host
2480 static void
2481 authenticate(
2482 struct parse *pcmd,
2483 FILE *fp
2486 if (pcmd->nargs == 0) {
2487 if (always_auth) {
2488 (void) fprintf(fp,
2489 "authenticated requests being sent\n");
2490 } else
2491 (void) fprintf(fp,
2492 "unauthenticated requests being sent\n");
2493 } else {
2494 if (STREQ(pcmd->argval[0].string, "yes")) {
2495 always_auth = 1;
2496 } else if (STREQ(pcmd->argval[0].string, "no")) {
2497 always_auth = 0;
2498 } else
2499 (void)fprintf(stderr, "What?\n");
2505 * ntpversion - choose the NTP version to use
2507 static void
2508 ntpversion(
2509 struct parse *pcmd,
2510 FILE *fp
2513 if (pcmd->nargs == 0) {
2514 (void) fprintf(fp,
2515 "NTP version being claimed is %d\n", pktversion);
2516 } else {
2517 if (pcmd->argval[0].uval < NTP_OLDVERSION
2518 || pcmd->argval[0].uval > NTP_VERSION) {
2519 (void) fprintf(stderr, "versions %d to %d, please\n",
2520 NTP_OLDVERSION, NTP_VERSION);
2521 } else {
2522 pktversion = (u_char) pcmd->argval[0].uval;
2529 * warning - print a warning message
2531 static void
2532 warning(
2533 const char *fmt,
2534 const char *st1,
2535 const char *st2
2538 (void) fprintf(stderr, "%s: ", progname);
2539 (void) fprintf(stderr, fmt, st1, st2);
2540 (void) fprintf(stderr, ": ");
2541 perror("");
2546 * error - print a message and exit
2548 static void
2549 error(
2550 const char *fmt,
2551 const char *st1,
2552 const char *st2
2555 warning(fmt, st1, st2);
2556 exit(1);
2560 * getkeyid - prompt the user for a keyid to use
2562 static u_long
2563 getkeyid(
2564 const char *keyprompt
2567 register char *p;
2568 register int c;
2569 FILE *fi;
2570 char pbuf[20];
2572 #ifndef SYS_WINNT
2573 if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
2574 #else
2575 if ((fi = _fdopen((int)GetStdHandle(STD_INPUT_HANDLE), "r")) == NULL)
2576 #endif /* SYS_WINNT */
2577 fi = stdin;
2578 else
2579 setbuf(fi, (char *)NULL);
2580 fprintf(stderr, "%s", keyprompt); fflush(stderr);
2581 for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
2582 if (p < &pbuf[18])
2583 *p++ = (char)c;
2585 *p = '\0';
2586 if (fi != stdin)
2587 fclose(fi);
2588 if (strcmp(pbuf, "0") == 0)
2589 return 0;
2591 return (u_long) atoi(pbuf);
2596 * atoascii - printable-ize possibly ascii data using the character
2597 * transformations cat -v uses.
2599 static void
2600 atoascii(
2601 int length,
2602 char *data,
2603 char *outdata
2606 register u_char *cp;
2607 register u_char *ocp;
2608 register u_char c;
2610 if (!data)
2612 *outdata = '\0';
2613 return;
2616 ocp = (u_char *)outdata;
2617 for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
2618 c = *cp;
2619 if (c == '\0')
2620 break;
2621 if (c == '\0')
2622 break;
2623 if (c > 0177) {
2624 *ocp++ = 'M';
2625 *ocp++ = '-';
2626 c &= 0177;
2629 if (c < ' ') {
2630 *ocp++ = '^';
2631 *ocp++ = (u_char)(c + '@');
2632 } else if (c == 0177) {
2633 *ocp++ = '^';
2634 *ocp++ = '?';
2635 } else {
2636 *ocp++ = c;
2638 if (ocp >= ((u_char *)outdata + length - 4))
2639 break;
2641 *ocp++ = '\0';
2647 * makeascii - print possibly ascii data using the character
2648 * transformations that cat -v uses.
2650 static void
2651 makeascii(
2652 int length,
2653 char *data,
2654 FILE *fp
2657 register u_char *cp;
2658 register int c;
2660 for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
2661 c = (int)*cp;
2662 if (c > 0177) {
2663 putc('M', fp);
2664 putc('-', fp);
2665 c &= 0177;
2668 if (c < ' ') {
2669 putc('^', fp);
2670 putc(c+'@', fp);
2671 } else if (c == 0177) {
2672 putc('^', fp);
2673 putc('?', fp);
2674 } else {
2675 putc(c, fp);
2682 * asciize - same thing as makeascii except add a newline
2684 void
2685 asciize(
2686 int length,
2687 char *data,
2688 FILE *fp
2691 makeascii(length, data, fp);
2692 putc('\n', fp);
2697 * Some circular buffer space
2699 #define CBLEN 80
2700 #define NUMCB 6
2702 char circ_buf[NUMCB][CBLEN];
2703 int nextcb = 0;
2706 * nextvar - find the next variable in the buffer
2709 nextvar(
2710 int *datalen,
2711 char **datap,
2712 char **vname,
2713 char **vvalue
2716 register char *cp;
2717 register char *np;
2718 register char *cpend;
2719 register char *npend; /* character after last */
2720 int quoted = 0;
2721 static char name[MAXVARLEN];
2722 static char value[MAXVALLEN];
2724 cp = *datap;
2725 cpend = cp + *datalen;
2728 * Space past commas and white space
2730 while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
2731 cp++;
2732 if (cp == cpend)
2733 return 0;
2736 * Copy name until we hit a ',', an '=', a '\r' or a '\n'. Backspace
2737 * over any white space and terminate it.
2739 np = name;
2740 npend = &name[MAXVARLEN];
2741 while (cp < cpend && np < npend && *cp != ',' && *cp != '='
2742 && *cp != '\r' && *cp != '\n')
2743 *np++ = *cp++;
2745 * Check if we ran out of name space, without reaching the end or a
2746 * terminating character
2748 if (np == npend && !(cp == cpend || *cp == ',' || *cp == '=' ||
2749 *cp == '\r' || *cp == '\n'))
2750 return 0;
2751 while (isspace((int)(*(np-1))))
2752 np--;
2753 *np = '\0';
2754 *vname = name;
2757 * Check if we hit the end of the buffer or a ','. If so we are done.
2759 if (cp == cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
2760 if (cp != cpend)
2761 cp++;
2762 *datap = cp;
2763 *datalen = cpend - cp;
2764 *vvalue = (char *)0;
2765 return 1;
2769 * So far, so good. Copy out the value
2771 cp++; /* past '=' */
2772 while (cp < cpend && (isspace((int)*cp) && *cp != '\r' && *cp != '\n'))
2773 cp++;
2774 np = value;
2775 npend = &value[MAXVALLEN];
2776 while (cp < cpend && np < npend && ((*cp != ',') || quoted))
2778 quoted ^= ((*np++ = *cp++) == '"');
2782 * Check if we overran the value buffer while still in a quoted string
2783 * or without finding a comma
2785 if (np == npend && (quoted || *cp != ','))
2786 return 0;
2788 * Trim off any trailing whitespace
2790 while (np > value && isspace((int)(*(np-1))))
2791 np--;
2792 *np = '\0';
2795 * Return this. All done.
2797 if (cp != cpend)
2798 cp++;
2799 *datap = cp;
2800 *datalen = cpend - cp;
2801 *vvalue = value;
2802 return 1;
2807 * findvar - see if this variable is known to us.
2808 * If "code" is 1, return ctl_var->code.
2809 * Otherwise return the ordinal position of the found variable.
2812 findvar(
2813 char *varname,
2814 struct ctl_var *varlist,
2815 int code
2818 register char *np;
2819 register struct ctl_var *vl;
2821 vl = varlist;
2822 np = varname;
2823 while (vl->fmt != EOV) {
2824 if (vl->fmt != PADDING && STREQ(np, vl->text))
2825 return (code)
2826 ? vl->code
2827 : (vl - varlist)
2829 vl++;
2831 return 0;
2837 * printvars - print variables returned in response packet
2839 void
2840 printvars(
2841 int length,
2842 char *data,
2843 int status,
2844 int sttype,
2845 FILE *fp
2848 if (rawmode)
2849 rawprint(sttype, length, data, status, fp);
2850 else
2851 cookedprint(sttype, length, data, status, fp);
2856 * rawprint - do a printout of the data in raw mode
2858 static void
2859 rawprint(
2860 int datatype,
2861 int length,
2862 char *data,
2863 int status,
2864 FILE *fp
2867 register char *cp;
2868 register char *cpend;
2871 * Essentially print the data as is. We reformat unprintables, though.
2873 cp = data;
2874 cpend = data + length;
2876 (void) fprintf(fp, "status=0x%04x,\n", status);
2878 while (cp < cpend) {
2879 if (*cp == '\r') {
2881 * If this is a \r and the next character is a
2882 * \n, supress this, else pretty print it. Otherwise
2883 * just output the character.
2885 if (cp == (cpend-1) || *(cp+1) != '\n')
2886 makeascii(1, cp, fp);
2887 } else if (isspace((int)*cp) || isprint((int)*cp)) {
2888 putc(*cp, fp);
2889 } else {
2890 makeascii(1, cp, fp);
2892 cp++;
2898 * Global data used by the cooked output routines
2900 int out_chars; /* number of characters output */
2901 int out_linecount; /* number of characters output on this line */
2905 * startoutput - get ready to do cooked output
2907 static void
2908 startoutput(void)
2910 out_chars = 0;
2911 out_linecount = 0;
2916 * output - output a variable=value combination
2918 static void
2919 output(
2920 FILE *fp,
2921 char *name,
2922 char *value
2925 int lenname;
2926 int lenvalue;
2928 lenname = strlen(name);
2929 lenvalue = strlen(value);
2931 if (out_chars != 0) {
2932 putc(',', fp);
2933 out_chars++;
2934 out_linecount++;
2935 if ((out_linecount + lenname + lenvalue + 3) > MAXOUTLINE) {
2936 putc('\n', fp);
2937 out_chars++;
2938 out_linecount = 0;
2939 } else {
2940 putc(' ', fp);
2941 out_chars++;
2942 out_linecount++;
2946 fputs(name, fp);
2947 putc('=', fp);
2948 fputs(value, fp);
2949 out_chars += lenname + 1 + lenvalue;
2950 out_linecount += lenname + 1 + lenvalue;
2955 * endoutput - terminate a block of cooked output
2957 static void
2958 endoutput(
2959 FILE *fp
2962 if (out_chars != 0)
2963 putc('\n', fp);
2968 * outputarr - output an array of values
2970 static void
2971 outputarr(
2972 FILE *fp,
2973 char *name,
2974 int narr,
2975 l_fp *lfp
2978 register char *bp;
2979 register char *cp;
2980 register int i;
2981 register int len;
2982 char buf[256];
2984 bp = buf;
2986 * Hack to align delay and offset values
2988 for (i = (int)strlen(name); i < 11; i++)
2989 *bp++ = ' ';
2991 for (i = narr; i > 0; i--) {
2992 if (i != narr)
2993 *bp++ = ' ';
2994 cp = lfptoms(lfp, 2);
2995 len = strlen(cp);
2996 if (len > 7) {
2997 cp[7] = '\0';
2998 len = 7;
3000 while (len < 7) {
3001 *bp++ = ' ';
3002 len++;
3004 while (*cp != '\0')
3005 *bp++ = *cp++;
3006 lfp++;
3008 *bp = '\0';
3009 output(fp, name, buf);
3012 static char *
3013 tstflags(
3014 u_long val
3017 register char *cb, *s;
3018 register int i;
3019 register const char *sep;
3021 sep = "";
3022 i = 0;
3023 s = cb = &circ_buf[nextcb][0];
3024 if (++nextcb >= NUMCB)
3025 nextcb = 0;
3027 sprintf(cb, "%02lx", val);
3028 cb += strlen(cb);
3029 if (!val) {
3030 strcat(cb, " ok");
3031 cb += strlen(cb);
3032 } else {
3033 *cb++ = ' ';
3034 for (i = 0; i < 13; i++) {
3035 if (val & 0x1) {
3036 sprintf(cb, "%s%s", sep, tstflagnames[i]);
3037 sep = ", ";
3038 cb += strlen(cb);
3040 val >>= 1;
3043 *cb = '\0';
3044 return s;
3048 * cookedprint - output variables in cooked mode
3050 static void
3051 cookedprint(
3052 int datatype,
3053 int length,
3054 char *data,
3055 int status,
3056 FILE *fp
3059 register int varid;
3060 char *name;
3061 char *value;
3062 char output_raw;
3063 int fmt;
3064 struct ctl_var *varlist;
3065 l_fp lfp;
3066 long ival;
3067 struct sockaddr_storage hval;
3068 u_long uval;
3069 l_fp lfparr[8];
3070 int narr;
3072 switch (datatype) {
3073 case TYPE_PEER:
3074 varlist = peer_var;
3075 break;
3076 case TYPE_SYS:
3077 varlist = sys_var;
3078 break;
3079 case TYPE_CLOCK:
3080 varlist = clock_var;
3081 break;
3082 default:
3083 (void) fprintf(stderr, "Unknown datatype(0x%x) in cookedprint\n", datatype);
3084 return;
3087 (void) fprintf(fp, "status=%04x %s,\n", status,
3088 statustoa(datatype, status));
3090 startoutput();
3091 while (nextvar(&length, &data, &name, &value)) {
3092 varid = findvar(name, varlist, 0);
3093 if (varid == 0) {
3094 output_raw = '*';
3095 } else {
3096 output_raw = 0;
3097 fmt = varlist[varid].fmt;
3098 switch(fmt) {
3099 case TS:
3100 if (!decodets(value, &lfp))
3101 output_raw = '?';
3102 else
3103 output(fp, name, prettydate(&lfp));
3104 break;
3105 case FL:
3106 case FU:
3107 case FS:
3108 if (!decodetime(value, &lfp))
3109 output_raw = '?';
3110 else {
3111 switch (fmt) {
3112 case FL:
3113 output(fp, name,
3114 lfptoms(&lfp, 3));
3115 break;
3116 case FU:
3117 output(fp, name,
3118 ulfptoms(&lfp, 3));
3119 break;
3120 case FS:
3121 output(fp, name,
3122 lfptoms(&lfp, 3));
3123 break;
3126 break;
3128 case UI:
3129 if (!decodeuint(value, &uval))
3130 output_raw = '?';
3131 else
3132 output(fp, name, uinttoa(uval));
3133 break;
3135 case SI:
3136 if (!decodeint(value, &ival))
3137 output_raw = '?';
3138 else
3139 output(fp, name, inttoa(ival));
3140 break;
3142 case HA:
3143 case NA:
3144 if (!decodenetnum(value, &hval))
3145 output_raw = '?';
3146 else if (fmt == HA){
3147 output(fp, name, nntohost(&hval));
3148 } else {
3149 output(fp, name, stoa(&hval));
3151 break;
3153 case ST:
3154 output_raw = '*';
3155 break;
3157 case RF:
3158 if (decodenetnum(value, &hval)) {
3159 if ((hval.ss_family == AF_INET) &&
3160 ISREFCLOCKADR(&hval))
3161 output(fp, name,
3162 refnumtoa(&hval));
3163 else
3164 output(fp, name, stoa(&hval));
3165 } else if ((int)strlen(value) <= 4)
3166 output(fp, name, value);
3167 else
3168 output_raw = '?';
3169 break;
3171 case LP:
3172 if (!decodeuint(value, &uval) || uval > 3)
3173 output_raw = '?';
3174 else {
3175 char b[3];
3176 b[0] = b[1] = '0';
3177 if (uval & 0x2)
3178 b[0] = '1';
3179 if (uval & 0x1)
3180 b[1] = '1';
3181 b[2] = '\0';
3182 output(fp, name, b);
3184 break;
3186 case OC:
3187 if (!decodeuint(value, &uval))
3188 output_raw = '?';
3189 else {
3190 char b[12];
3192 (void) snprintf(b, sizeof(b), "%03lo",
3193 uval);
3194 output(fp, name, b);
3196 break;
3198 case MD:
3199 if (!decodeuint(value, &uval))
3200 output_raw = '?';
3201 else
3202 output(fp, name, uinttoa(uval));
3203 break;
3205 case AR:
3206 if (!decodearr(value, &narr, lfparr))
3207 output_raw = '?';
3208 else
3209 outputarr(fp, name, narr, lfparr);
3210 break;
3212 case FX:
3213 if (!decodeuint(value, &uval))
3214 output_raw = '?';
3215 else
3216 output(fp, name, tstflags(uval));
3217 break;
3219 default:
3220 (void) fprintf(stderr,
3221 "Internal error in cookedprint, %s=%s, fmt %d\n",
3222 name, value, fmt);
3223 break;
3227 if (output_raw != 0) {
3228 char bn[401];
3229 char bv[401];
3230 int len;
3232 atoascii(400, name, bn);
3233 atoascii(400, value, bv);
3234 if (output_raw != '*') {
3235 len = strlen(bv);
3236 bv[len] = output_raw;
3237 bv[len+1] = '\0';
3239 output(fp, bn, bv);
3242 endoutput(fp);
3247 * sortassoc - sort associations in the cache into ascending order
3249 void
3250 sortassoc(void)
3252 if (numassoc > 1)
3253 qsort(
3254 #ifdef QSORT_USES_VOID_P
3255 (void *)
3256 #else
3257 (char *)
3258 #endif
3259 assoc_cache, (size_t)numassoc,
3260 sizeof(struct association), assoccmp);
3265 * assoccmp - compare two associations
3267 #ifdef QSORT_USES_VOID_P
3268 static int
3269 assoccmp(
3270 const void *t1,
3271 const void *t2
3274 const struct association *ass1 = (const struct association *)t1;
3275 const struct association *ass2 = (const struct association *)t2;
3277 if (ass1->assid < ass2->assid)
3278 return -1;
3279 if (ass1->assid > ass2->assid)
3280 return 1;
3281 return 0;
3283 #else
3284 static int
3285 assoccmp(
3286 struct association *ass1,
3287 struct association *ass2
3290 if (ass1->assid < ass2->assid)
3291 return -1;
3292 if (ass1->assid > ass2->assid)
3293 return 1;
3294 return 0;
3296 #endif /* not QSORT_USES_VOID_P */