2 * ntpq - query an NTP server using mode 6 commands
10 #include <sys/types.h>
22 #include <isc/result.h>
25 #include "ntp_assert.h"
26 #include "ntp_stdlib.h"
27 #include "ntp_unixtime.h"
28 #include "ntp_calendar.h"
29 #include "ntp_select.h"
30 #include "ntp_assert.h"
31 #include "lib_strbuf.h"
32 #include "ntp_lineedit.h"
33 #include "ntp_debug.h"
35 #include "openssl/evp.h"
36 #include "openssl/objects.h"
37 #include "openssl/err.h"
39 # include "openssl/opensslv.h"
40 # if !defined(HAVE_EVP_MD_DO_ALL_SORTED) && OPENSSL_VERSION_NUMBER > 0x10000000L
41 # define HAVE_EVP_MD_DO_ALL_SORTED 1
44 #include "libssl_compat.h"
46 #define CMAC "AES128CMAC"
48 #include <ssl_applink.c>
50 #include "ntp_libopts.h"
53 #ifdef SYS_VXWORKS /* vxWorks needs mode flag -casey*/
54 # define open(name, flags) open(name, flags, 0777)
55 # define SERVER_PORT_NUM 123
58 /* we use COMMAND as an autogen keyword */
64 * Because we potentially understand a lot of commands we will run
65 * interactive if connected to a terminal.
67 int interactive = 0; /* set to 1 when we should prompt */
68 const char *prompt = "ntpq> "; /* prompt to ask him about */
71 * use old readvars behavior? --old-rv processing in ntpq resets
72 * this value based on the presence or absence of --old-rv. It is
73 * initialized to 1 here to maintain backward compatibility with
74 * libntpq clients such as ntpsnmpd, which are free to reset it as
80 * How should we display the refid?
81 * REFID_HASH, REFID_IPV4
88 s_char sys_precision; /* local clock precision (log2 s) */
91 * Keyid used for authenticated requests. Obtained on the fly.
93 u_long info_auth_keyid = 0;
95 static int info_auth_keytype = NID_md5; /* MD5 */
96 static size_t info_auth_hashlen = 16; /* MD5 */
97 u_long current_time; /* needed by authkeys; not used */
100 * Flag which indicates we should always send authenticated requests
105 * Flag which indicates raw mode output.
110 * Packet version number we use
112 u_char pktversion = NTP_OLDVERSION + 1;
115 * Don't jump if no set jmp.
117 volatile int jump = 0;
123 #define HA 1 /* host address */
124 #define NA 2 /* network address */
125 #define LP 3 /* leap (print in binary) */
126 #define RF 4 /* refid (sometimes string, sometimes not) */
127 #define AR 5 /* array of times */
128 #define FX 6 /* test flags */
129 #define TS 7 /* l_fp timestamp in hex */
130 #define OC 8 /* integer, print in octal */
131 #define EOV 255 /* end of table */
134 * For the most part ntpq simply displays what ntpd provides in the
135 * mostly plain-text mode 6 responses. A few variable names are by
136 * default "cooked" to provide more human-friendly output.
138 const var_format cookedvars[] = {
149 { "peeradr", HA }, /* compat with others */
152 { "filtoffset", AR },
154 { "filterror", AR }, /* compat with others */
162 static const char *tstflagnames[] = {
163 "pkt_dup", /* TEST1 */
164 "pkt_bogus", /* TEST2 */
165 "pkt_unsync", /* TEST3 */
166 "pkt_denied", /* TEST4 */
167 "pkt_auth", /* TEST5 */
168 "pkt_stratum", /* TEST6 */
169 "pkt_header", /* TEST7 */
170 "pkt_autokey", /* TEST8 */
171 "pkt_crypto", /* TEST9 */
172 "peer_stratum", /* TEST10 */
173 "peer_dist", /* TEST11 */
174 "peer_loop", /* TEST12 */
175 "peer_unreach" /* TEST13 */
179 int ntpqmain (int, char **);
181 * Built in command handler declarations
183 static int openhost (const char *, int);
184 static void dump_hex_printable(const void *, size_t);
185 static int sendpkt (void *, size_t);
186 static int getresponse (int, int, u_short *, size_t *, const char **, int);
187 static int sendrequest (int, associd_t, int, size_t, const char *);
188 static char * tstflags (u_long);
190 static void getcmds (void);
192 static int abortcmd (void);
193 #endif /* SYS_WINNT */
194 static void docmd (const char *);
195 static void tokenize (const char *, char **, int *);
196 static int getarg (const char *, int, arg_v *);
197 #endif /* BUILD_AS_LIB */
198 static int findcmd (const char *, struct xcmd *,
199 struct xcmd *, struct xcmd **);
200 static int rtdatetolfp (char *, l_fp *);
201 static int decodearr (char *, int *, l_fp *, int);
202 static void help (struct parse *, FILE *);
203 static int helpsort (const void *, const void *);
204 static void printusage (struct xcmd *, FILE *);
205 static void timeout (struct parse *, FILE *);
206 static void auth_delay (struct parse *, FILE *);
207 static void host (struct parse *, FILE *);
208 static void ntp_poll (struct parse *, FILE *);
209 static void keyid (struct parse *, FILE *);
210 static void keytype (struct parse *, FILE *);
211 static void passwd (struct parse *, FILE *);
212 static void hostnames (struct parse *, FILE *);
213 static void setdebug (struct parse *, FILE *);
214 static void quit (struct parse *, FILE *);
215 static void showdrefid (struct parse *, FILE *);
216 static void version (struct parse *, FILE *);
217 static void raw (struct parse *, FILE *);
218 static void cooked (struct parse *, FILE *);
219 static void authenticate (struct parse *, FILE *);
220 static void ntpversion (struct parse *, FILE *);
221 static void warning (const char *, ...)
222 __attribute__((__format__(__printf__, 1, 2)));
223 static void error (const char *, ...)
224 __attribute__((__format__(__printf__, 1, 2)));
225 static u_long getkeyid (const char *);
226 static void atoascii (const char *, size_t, char *, size_t);
227 static void cookedprint (int, size_t, const char *, int, int, FILE *);
228 static void rawprint (int, size_t, const char *, int, int, FILE *);
229 static void startoutput (void);
230 static void output (FILE *, const char *, const char *);
231 static void endoutput (FILE *);
232 static void outputarr (FILE *, char *, int, l_fp *);
233 static int assoccmp (const void *, const void *);
234 static void on_ctrlc (void);
235 u_short varfmt (const char *);
236 static int my_easprintf (char**, const char *, ...) NTP_PRINTF(2, 3);
237 void ntpq_custom_opt_handler (tOptions *, tOptDesc *);
239 /* read a character from memory and expand to integer */
245 return (int)*(const unsigned char*)cp;
250 # ifdef HAVE_EVP_MD_DO_ALL_SORTED
251 static void list_md_fn(const EVP_MD *m, const char *from,
252 const char *to, void *arg );
255 static char *insert_cmac(char *list);
256 static char *list_digest_names(void);
259 * Built-in commands we understand
261 struct xcmd builtins[] = {
262 { "?", help, { OPT|NTP_STR, NO, NO, NO },
263 { "command", "", "", "" },
264 "tell the use and syntax of commands" },
265 { "help", help, { OPT|NTP_STR, NO, NO, NO },
266 { "command", "", "", "" },
267 "tell the use and syntax of commands" },
268 { "timeout", timeout, { OPT|NTP_UINT, NO, NO, NO },
269 { "msec", "", "", "" },
270 "set the primary receive time out" },
271 { "delay", auth_delay, { OPT|NTP_INT, NO, NO, NO },
272 { "msec", "", "", "" },
273 "set the delay added to encryption time stamps" },
274 { "host", host, { OPT|NTP_STR, OPT|NTP_STR, NO, NO },
275 { "-4|-6", "hostname", "", "" },
276 "specify the host whose NTP server we talk to" },
277 { "poll", ntp_poll, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
278 { "n", "verbose", "", "" },
279 "poll an NTP server in client mode `n' times" },
280 { "passwd", passwd, { OPT|NTP_STR, NO, NO, NO },
282 "specify a password to use for authenticated requests"},
283 { "hostnames", hostnames, { OPT|NTP_STR, NO, NO, NO },
284 { "yes|no", "", "", "" },
285 "specify whether hostnames or net numbers are printed"},
286 { "debug", setdebug, { OPT|NTP_STR, NO, NO, NO },
287 { "no|more|less", "", "", "" },
288 "set/change debugging level" },
289 { "quit", quit, { NO, NO, NO, NO },
292 { "exit", quit, { NO, NO, NO, NO },
295 { "keyid", keyid, { OPT|NTP_UINT, NO, NO, NO },
296 { "key#", "", "", "" },
297 "set keyid to use for authenticated requests" },
298 { "drefid", showdrefid, { OPT|NTP_STR, NO, NO, NO },
299 { "hash|ipv4", "", "", "" },
300 "display refid's as IPv4 or hash" },
301 { "version", version, { NO, NO, NO, NO },
303 "print version number" },
304 { "raw", raw, { NO, NO, NO, NO },
306 "do raw mode variable output" },
307 { "cooked", cooked, { NO, NO, NO, NO },
309 "do cooked mode variable output" },
310 { "authenticate", authenticate, { OPT|NTP_STR, NO, NO, NO },
311 { "yes|no", "", "", "" },
312 "always authenticate requests to this server" },
313 { "ntpversion", ntpversion, { OPT|NTP_UINT, NO, NO, NO },
314 { "version number", "", "", "" },
315 "set the NTP version number to use for requests" },
316 { "keytype", keytype, { OPT|NTP_STR, NO, NO, NO },
317 { "key type %s", "", "", "" },
319 { 0, 0, { NO, NO, NO, NO },
320 { "", "", "", "" }, "" }
325 * Default values we use.
327 #define DEFHOST "localhost" /* default host name */
328 #define DEFTIMEOUT 5 /* wait 5 seconds for 1st pkt */
329 #define DEFSTIMEOUT 3 /* and 3 more for each additional */
331 * Requests are automatically retried once, so total timeout with no
332 * response is a bit over 2 * DEFTIMEOUT, or 10 seconds. At the other
333 * extreme, a request eliciting 32 packets of responses each for some
334 * reason nearly DEFSTIMEOUT seconds after the prior in that series,
335 * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
336 * 93 seconds to fail each of two times, or 186 seconds.
337 * Some commands involve a series of requests, such as "peers" and
338 * "mrulist", so the cumulative timeouts are even longer for those.
340 #define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
341 #define LENHOSTNAME 256 /* host name is 256 characters long */
342 #define MAXCMDS 100 /* maximum commands on cmd line */
343 #define MAXHOSTS 200 /* maximum hosts on cmd line */
344 #define MAXLINE 512 /* maximum line length */
345 #define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */
346 #define MAXVARLEN 256 /* maximum length of a variable name */
347 #define MAXVALLEN 2048 /* maximum length of a variable value */
348 #define MAXOUTLINE 72 /* maximum length of an output line */
349 #define SCREENWIDTH 76 /* nominal screen width in columns */
352 * Some variables used and manipulated locally
354 struct sock_timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */
355 struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
356 l_fp delay_time; /* delay time */
357 char currenthost[LENHOSTNAME]; /* current host name */
358 int currenthostisnum; /* is prior text from IP? */
359 struct sockaddr_in hostaddr; /* host address */
360 int showhostnames = 1; /* show host names by default */
361 int wideremote = 0; /* show wide remote names? */
363 int ai_fam_templ; /* address family */
364 int ai_fam_default; /* default address family */
365 SOCKET sockfd; /* fd socket is opened on */
366 int havehost = 0; /* set to 1 when host open */
368 struct servent *server_entry = NULL; /* server entry for ntp */
372 * Sequence number used for requests. It is incremented before
378 * Holds data returned from queries. Declare buffer long to be sure of
381 #define DATASIZE (MAXFRAGS*480) /* maximum amount of data */
382 long pktdata[DATASIZE/sizeof(long)];
385 * assoc_cache[] is a dynamic array which allows references to
386 * associations using &1 ... &N for n associations, avoiding manual
387 * lookup of the current association IDs for a given ntpd. It also
388 * caches the status word for each association, retrieved incidentally.
390 struct association * assoc_cache;
391 u_int assoc_cache_slots;/* count of allocated array entries */
392 u_int numassoc; /* number of cached associations */
395 * For commands typed on the command line (with the -c option)
398 const char *ccmds[MAXCMDS];
399 #define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
402 * When multiple hosts are specified.
407 chost chosts[MAXHOSTS];
408 #define ADDHOST(cp) \
410 if (numhosts < MAXHOSTS) { \
411 chosts[numhosts].name = (cp); \
412 chosts[numhosts].fam = ai_fam_templ; \
418 * Macro definitions we use
420 #define ISSPACE(c) ((c) == ' ' || (c) == '\t')
421 #define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0')
422 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
425 * Jump buffer for longjumping back to the command level
427 jmp_buf interrupt_buf;
430 * Points at file being currently printed into
432 FILE *current_output;
435 * Command table imported from ntpdc_ops.c
437 extern struct xcmd opcmds[];
439 char const *progname;
441 #ifdef NO_MAIN_ALLOWED
443 CALL(ntpq,"ntpq",ntpqmain);
445 void clear_globals(void)
447 extern int ntp_optind;
448 showhostnames = 0; /* don'tshow host names by default */
450 server_entry = NULL; /* server entry for ntp */
451 havehost = 0; /* set to 1 when host open */
452 numassoc = 0; /* number of cached associations */
456 #endif /* !BUILD_AS_LIB */
457 #endif /* NO_MAIN_ALLOWED */
460 * main - parse arguments and handle options
462 #ifndef NO_MAIN_ALLOWED
469 return ntpqmain(argc, argv);
487 taskPrioritySet(taskIdSelf(), 100 );
491 delay_time.l_uf = DEFDELAY;
493 init_lib(); /* sets up ipv4_works, ipv6_works */
497 /* Check to see if we have IPv6. Otherwise default to IPv4 */
499 ai_fam_default = AF_INET;
501 /* Fixup keytype's help based on available digest names */
507 list = list_digest_names();
509 for (icmd = 0; icmd < sizeof(builtins)/sizeof(*builtins); icmd++) {
510 if (strcmp("keytype", builtins[icmd].keyword) == 0) {
516 /* This should only "trip" if "keytype" is removed from builtins */
517 INSIST(icmd < sizeof(builtins)/sizeof(*builtins));
520 builtins[icmd].desc[0] = "digest-name";
522 "set key type to use for authenticated requests, one of:%s",
525 builtins[icmd].desc[0] = "md5";
527 "set key type to use for authenticated requests (%s)",
530 builtins[icmd].comment = msg;
537 int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
543 * Process options other than -c and -p, which are specially
544 * handled by ntpq_custom_opt_handler().
547 debug = OPT_VALUE_SET_DEBUG_LEVEL;
550 ai_fam_templ = AF_INET;
551 else if (HAVE_OPT(IPV6))
552 ai_fam_templ = AF_INET6;
554 ai_fam_templ = ai_fam_default;
556 if (HAVE_OPT(INTERACTIVE))
559 if (HAVE_OPT(NUMERIC))
565 old_rv = HAVE_OPT(OLD_RV);
567 drefid = OPT_VALUE_REFID;
572 for (ihost = 0; ihost < (u_int)argc; ihost++) {
573 if ('-' == *argv[ihost]) {
575 // If I really cared I'd also check:
576 // 0 == argv[ihost][2]
578 // and there are other cases as well...
580 if ('4' == argv[ihost][1]) {
581 ai_fam_templ = AF_INET;
583 } else if ('6' == argv[ihost][1]) {
584 ai_fam_templ = AF_INET6;
587 // XXX Throw a usage error
590 ADDHOST(argv[ihost]);
594 if (numcmds == 0 && interactive == 0
595 && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
599 set_ctrl_c_hook(on_ctrlc);
600 #ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
602 push_ctrl_c_handler(abortcmd);
603 #endif /* SYS_WINNT */
606 (void) openhost(chosts[0].name, chosts[0].fam);
609 for (ihost = 0; ihost < numhosts; ihost++) {
610 if (openhost(chosts[ihost].name, chosts[ihost].fam)) {
612 fputc('\n', current_output);
613 for (icmd = 0; icmd < numcmds; icmd++) {
615 fputc('\n', current_output);
623 #endif /* SYS_WINNT */
626 #endif /* !BUILD_AS_LIB */
629 * openhost - open a socket to a host
637 const char svc[] = "ntp";
638 char temphost[LENHOSTNAME];
640 struct addrinfo hints, *ai;
643 register const char *cp;
644 char name[LENHOSTNAME];
647 * We need to get by the [] if they were entered
654 for (i = 0; *cp && *cp != ']'; cp++, i++)
665 * First try to resolve it as an ip address and if that fails,
666 * do a fullblown (dns) lookup. That way we only use the dns
667 * when it is needed and work around some implementations that
668 * will return an "IPv4-mapped IPv6 address" address if you
669 * give it an IPv4 address to lookup.
672 hints.ai_family = fam;
673 hints.ai_protocol = IPPROTO_UDP;
674 hints.ai_socktype = SOCK_DGRAM;
675 hints.ai_flags = Z_AI_NUMERICHOST;
678 a_info = getaddrinfo(hname, svc, &hints, &ai);
679 if (a_info == EAI_NONAME
681 || a_info == EAI_NODATA
684 hints.ai_flags = AI_CANONNAME;
686 hints.ai_flags |= AI_ADDRCONFIG;
688 a_info = getaddrinfo(hname, svc, &hints, &ai);
691 /* Some older implementations don't like AI_ADDRCONFIG. */
692 if (a_info == EAI_BADFLAGS) {
693 hints.ai_flags &= ~AI_ADDRCONFIG;
694 a_info = getaddrinfo(hname, svc, &hints, &ai);
698 fprintf(stderr, "%s\n", gai_strerror(a_info));
704 octets = min(sizeof(addr), ai->ai_addrlen);
705 memcpy(&addr, ai->ai_addr, octets);
707 if (ai->ai_canonname == NULL) {
708 strlcpy(temphost, stoa(&addr), sizeof(temphost));
709 currenthostisnum = TRUE;
711 strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
712 currenthostisnum = FALSE;
716 printf("Opening host %s (%s)\n",
718 (ai->ai_family == AF_INET)
720 : (ai->ai_family == AF_INET6)
727 printf("Closing old host %s\n", currenthost);
731 strlcpy(currenthost, temphost, sizeof(currenthost));
733 /* port maps to the same location in both families */
734 s_port = NSRCPORT(&addr);
736 ((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
737 if (ai->ai_family == AF_INET)
738 *(struct sockaddr_in *)&hostaddr=
739 *((struct sockaddr_in *)ai->ai_addr);
741 *(struct sockaddr_in6 *)&hostaddr=
742 *((struct sockaddr_in6 *)ai->ai_addr);
743 #endif /* SYS_VXWORKS */
747 int optionValue = SO_SYNCHRONOUS_NONALERT;
750 err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
751 (void *)&optionValue, sizeof(optionValue));
754 "setsockopt(SO_SYNCHRONOUS_NONALERT)"
760 #endif /* SYS_WINNT */
762 sockfd = socket(ai->ai_family, ai->ai_socktype,
764 if (sockfd == INVALID_SOCKET) {
771 #ifdef NEED_RCVBUF_SLOP
773 { int rbufsize = DATASIZE + 2048; /* 2K for slop */
774 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
775 (void *)&rbufsize, sizeof(int)) == -1)
783 (connect(sockfd, (struct sockaddr *)&hostaddr,
784 sizeof(hostaddr)) == -1)
786 (connect(sockfd, (struct sockaddr *)ai->ai_addr,
787 ai->ai_addrlen) == -1)
788 #endif /* SYS_VXWORKS */
808 /* every line shows at most 16 bytes, so we need a buffer of
809 * 4 * 16 (2 xdigits, 1 char, one sep for xdigits)
810 * + 2 * 1 (block separators)
815 static const char s_xdig[16] = "0123456789ABCDEF";
819 const u_char * cdata = data;
823 memset(lbuf, ' ', sizeof(lbuf));
825 pptr = lbuf + 3*16 + 2;
827 rowlen = (len > 16) ? 16 : (int)len;
833 *xptr++ = s_xdig[ch >> 4 ];
834 *xptr++ = s_xdig[ch & 0x0F];
835 if (++xptr == lbuf + 3*8)
838 *pptr++ = isprint(ch) ? (char)ch : '.';
848 /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
850 * sendpkt - send a packet to the remote host
859 printf("Sending %zu octets\n", xdatalen);
861 if (send(sockfd, xdata, xdatalen, 0) == -1) {
862 warning("write to %s failed", currenthost);
867 printf("Request packet:\n");
868 dump_hex_printable(xdata, xdatalen);
874 * getresponse - get a (series of) response packet(s) and return the data
886 struct ntp_control rpkt;
887 struct sock_timeval tvo;
888 u_short offsets[MAXFRAGS+1];
889 u_short counts[MAXFRAGS+1];
900 /* absolute timeout checks. Not 'time_t' by intention! */
901 uint32_t tobase; /* base value for timeout */
902 uint32_t tospan; /* timeout span (max delay) */
903 uint32_t todiff; /* current delay */
905 memset(offsets, 0, sizeof(offsets));
906 memset(counts , 0, sizeof(counts ));
909 * This is pretty tricky. We may get between 1 and MAXFRAG packets
910 * back in response to the request. We peel the data out of
911 * each packet and collect it in one long block. When the last
912 * packet in the sequence is received we'll know how much data we
913 * should have had. Note we use one long time out, should reconsider.
918 *rdata = (char *)pktdata;
923 tobase = (uint32_t)time(NULL);
928 * Loop until we have an error or a complete response. Nearly all
929 * code paths to loop again use continue.
937 tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
939 FD_SET(sockfd, &fds);
940 n = select(sockfd+1, &fds, NULL, NULL, &tvo);
942 #if !defined(SYS_WINNT) && defined(EINTR)
943 /* Windows does not know about EINTR (until very
944 * recently) and the handling of console events
945 * is *very* different from POSIX/UNIX signal
948 * Under non-windows targets we map EINTR as
949 * 'last packet was received' and try to exit
950 * the receive sequence.
952 if (errno == EINTR) {
957 warning("select fails");
962 * Check if this is already too late. Trash the data and
963 * fake a timeout if this is so.
965 todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
966 if ((n > 0) && (todiff > tospan)) {
967 n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
968 n -= n; /* faked timeout return from 'select()',
969 * execute RMW cycle on 'n'
975 * Timed out. Return what we have
980 "%s: timed out, nothing received\n",
986 "%s: timed out with incomplete data\n",
990 "ERR_INCOMPLETE: Received fragments:\n");
991 for (f = 0; f < numfrags; f++)
993 "%2u: %5d %5d\t%3d octets\n",
994 (u_int)f, offsets[f],
999 "last fragment %sreceived\n",
1004 return ERR_INCOMPLETE;
1007 n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
1014 printf("Response packet:\n");
1015 dump_hex_printable(&rpkt, n);
1019 * Check for format errors. Bug proofing.
1021 if (n < (int)CTL_HEADER_LEN) {
1023 printf("Short (%d byte) packet received\n", n);
1026 if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
1027 || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
1029 printf("Packet received with version %d\n",
1030 PKT_VERSION(rpkt.li_vn_mode));
1033 if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
1035 printf("Packet received with mode %d\n",
1036 PKT_MODE(rpkt.li_vn_mode));
1039 if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
1041 printf("Received request packet, wanted response\n");
1046 * Check opcode and sequence number for a match.
1047 * Could be old data getting to us.
1049 if (ntohs(rpkt.sequence) != sequence) {
1051 printf("Received sequnce number %d, wanted %d\n",
1052 ntohs(rpkt.sequence), sequence);
1055 if (CTL_OP(rpkt.r_m_e_op) != opcode) {
1058 "Received opcode %d, wanted %d (sequence number okay)\n",
1059 CTL_OP(rpkt.r_m_e_op), opcode);
1064 * Check the error code. If non-zero, return it.
1066 if (CTL_ISERROR(rpkt.r_m_e_op)) {
1067 errcode = (ntohs(rpkt.status) >> 8) & 0xff;
1068 if (CTL_ISMORE(rpkt.r_m_e_op))
1069 TRACE(1, ("Error code %d received on not-final packet\n",
1071 if (errcode == CERR_UNSPEC)
1077 * Check the association ID to make sure it matches what
1080 if (ntohs(rpkt.associd) != associd) {
1081 TRACE(1, ("Association ID %d doesn't match expected %d\n",
1082 ntohs(rpkt.associd), associd));
1084 * Hack for silly fuzzballs which, at the time of writing,
1085 * return an assID of sys.peer when queried for system variables.
1093 * Collect offset and count. Make sure they make sense.
1095 offset = ntohs(rpkt.offset);
1096 count = ntohs(rpkt.count);
1099 * validate received payload size is padded to next 32-bit
1100 * boundary and no smaller than claimed by rpkt.count
1103 TRACE(1, ("Response packet not padded, size = %d\n",
1108 shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
1110 if (n < shouldbesize) {
1111 printf("Response packet claims %u octets payload, above %ld received\n",
1112 count, (long)(n - CTL_HEADER_LEN));
1113 return ERR_INCOMPLETE;
1116 if (debug >= 3 && shouldbesize > n) {
1122 * Usually we ignore authentication, but for debugging purposes
1125 /* round to 8 octet boundary */
1126 shouldbesize = (shouldbesize + 7) & ~7;
1128 maclen = n - shouldbesize;
1129 if (maclen >= (int)MIN_MAC_LEN) {
1131 "Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1132 n, shouldbesize, maclen);
1133 lpkt = (u_int32 *)&rpkt;
1134 printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1135 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1136 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1137 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1138 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1139 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1140 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1141 key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1142 printf("Authenticated with keyid %lu\n", (u_long)key);
1143 if (key != 0 && key != info_auth_keyid) {
1144 printf("We don't know that key\n");
1146 if (authdecrypt(key, (u_int32 *)&rpkt,
1147 n - maclen, maclen)) {
1148 printf("Auth okay!\n");
1150 printf("Auth failed!\n");
1156 TRACE(2, ("Got packet, size = %d\n", n));
1157 if (count > (n - CTL_HEADER_LEN)) {
1158 TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
1159 count, (long)n - CTL_HEADER_LEN));
1162 if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1163 TRACE(1, ("Received count of 0 in non-final fragment\n"));
1166 if (offset + count > sizeof(pktdata)) {
1167 TRACE(1, ("Offset %u, count %u, too big for buffer\n",
1171 if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1172 TRACE(1, ("Received second last fragment packet\n"));
1177 * So far, so good. Record this fragment, making sure it doesn't
1180 TRACE(2, ("Packet okay\n"));
1182 if (numfrags > (MAXFRAGS - 1)) {
1183 TRACE(2, ("Number of fragments exceeds maximum %d\n",
1189 * Find the position for the fragment relative to any
1190 * previously received.
1193 f < numfrags && offsets[f] < offset;
1198 if (f < numfrags && offset == offsets[f]) {
1199 TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
1200 count, offset, counts[f], offsets[f]));
1204 if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
1205 TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
1206 offset, counts[f-1], offsets[f-1]));
1210 if (f < numfrags && (offset + count) > offsets[f]) {
1211 TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
1212 count, offset, offsets[f]));
1216 for (ff = numfrags; ff > f; ff--) {
1217 offsets[ff] = offsets[ff-1];
1218 counts[ff] = counts[ff-1];
1220 offsets[f] = offset;
1225 * Got that stuffed in right. Figure out if this was the last.
1226 * Record status info out of the last packet.
1228 if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1231 *rstatus = ntohs(rpkt.status);
1235 * Copy the data into the data buffer, and bump the
1236 * timout base in case we need more.
1238 memcpy((char *)pktdata + offset, &rpkt.u, count);
1239 tobase = (uint32_t)time(NULL);
1242 * If we've seen the last fragment, look for holes in the sequence.
1243 * If there aren't any, we're done.
1245 #if !defined(SYS_WINNT) && defined(EINTR)
1249 if (seenlastfrag && offsets[0] == 0) {
1250 for (f = 1; f < numfrags; f++)
1251 if (offsets[f-1] + counts[f-1] !=
1254 if (f == numfrags) {
1255 *rsize = offsets[f-1] + counts[f-1];
1256 TRACE(1, ("%lu packets reassembled into response\n",
1261 } /* giant for (;;) collecting response packets */
1262 } /* getresponse() */
1266 * sendrequest - format and send a request packet
1277 struct ntp_control qpkt;
1284 * Check to make sure the data will fit in one packet
1286 if (qsize > CTL_MAX_DATA_LEN) {
1288 "***Internal error! qsize (%zu) too large\n",
1294 * Fill in the packet
1296 qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1297 qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
1298 qpkt.sequence = htons(sequence);
1300 qpkt.associd = htons((u_short)associd);
1302 qpkt.count = htons((u_short)qsize);
1304 pktsize = CTL_HEADER_LEN;
1307 * If we have data, copy and pad it out to a 32-bit boundary.
1310 memcpy(&qpkt.u, qdata, (size_t)qsize);
1312 while (pktsize & (sizeof(u_int32) - 1)) {
1313 qpkt.u.data[qsize++] = 0;
1319 * If it isn't authenticated we can just send it. Otherwise
1320 * we're going to have to think about it a little.
1322 if (!auth && !always_auth) {
1323 return sendpkt(&qpkt, pktsize);
1327 * Pad out packet to a multiple of 8 octets to be sure
1328 * receiver can handle it.
1330 while (pktsize & 7) {
1331 qpkt.u.data[qsize++] = 0;
1336 * Get the keyid and the password if we don't have one.
1338 if (info_auth_keyid == 0) {
1339 key_id = getkeyid("Keyid: ");
1340 if (key_id == 0 || key_id > NTP_MAXKEY) {
1342 "Invalid key identifier\n");
1345 info_auth_keyid = key_id;
1347 if (!authistrusted(info_auth_keyid)) {
1348 pass = getpass_keytype(info_auth_keytype);
1349 if ('\0' == pass[0]) {
1350 fprintf(stderr, "Invalid password\n");
1353 authusekey(info_auth_keyid, info_auth_keytype,
1355 authtrust(info_auth_keyid, 1);
1359 * Do the encryption.
1361 maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1363 fprintf(stderr, "Key not found\n");
1365 } else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1367 "%zu octet MAC, %zu expected with %zu octet digest\n",
1368 maclen, (info_auth_hashlen + sizeof(keyid_t)),
1373 return sendpkt((char *)&qpkt, pktsize + maclen);
1378 * show_error_msg - display the error text for a mode 6 error response.
1387 fprintf(stderr, "server=%s ", currenthost);
1393 "***Server reports a bad format request packet\n");
1396 case CERR_PERMISSION:
1398 "***Server disallowed request (authentication?)\n");
1403 "***Server reports a bad opcode in request\n");
1408 "***Association ID %d unknown to server\n",
1412 case CERR_UNKNOWNVAR:
1414 "***A request variable unknown to the server\n");
1419 "***Server indicates a request variable was bad\n");
1424 "***Server returned an unspecified error\n");
1428 fprintf(stderr, "***Request timed out\n");
1431 case ERR_INCOMPLETE:
1433 "***Response from server was incomplete\n");
1438 "***Buffer size exceeded for returned data\n");
1443 "***Server returns unknown error code %d\n",
1449 * doquery - send a request and process the response, displaying
1450 * error messages for any error responses.
1464 return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
1465 rsize, rdata, FALSE);
1470 * doqueryex - send a request and process the response, optionally
1471 * displaying error messages for any error responses.
1490 * Check to make sure host is open
1493 fprintf(stderr, "***No host open, use `host' command\n");
1504 res = sendrequest(opcode, associd, auth, qsize, qdata);
1509 * Get the response. If we got a standard error, print a message
1511 res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1514 if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1515 if (res == ERR_INCOMPLETE) {
1517 * better bump the sequence so we don't
1518 * get confused about differing fragments.
1526 show_error_msg(res, associd);
1533 #ifndef BUILD_AS_LIB
1535 * getcmds - read commands from the standard input and execute them
1543 ntp_readline_init(interactive ? prompt : NULL);
1546 line = ntp_readline(&count);
1553 ntp_readline_uninit();
1555 #endif /* !BUILD_AS_LIB */
1558 #if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
1560 * abortcmd - catch interrupts and abort the current command
1565 if (current_output == stdout)
1566 (void) fflush(stdout);
1568 (void) fflush(stderr);
1571 longjmp(interrupt_buf, 1);
1575 #endif /* !SYS_WINNT && !BUILD_AS_LIB */
1578 #ifndef BUILD_AS_LIB
1580 * docmd - decode the command line and execute a command
1587 char *tokens[1+MAXARGS+2];
1594 * Tokenize the command line. If nothing on it, return.
1596 tokenize(cmdline, tokens, &ntok);
1601 * Find the appropriate command description.
1603 i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1605 (void) fprintf(stderr, "***Command `%s' unknown\n",
1608 } else if (i >= 2) {
1609 (void) fprintf(stderr, "***Command `%s' ambiguous\n",
1614 /* Warn about ignored extra args */
1615 for (i = MAXARGS + 1; i < ntok ; ++i) {
1616 fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
1620 * Save the keyword, then walk through the arguments, interpreting
1623 pcmd.keyword = tokens[0];
1625 for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1626 if ((i+1) >= ntok) {
1627 if (!(xcmd->arg[i] & OPT)) {
1628 printusage(xcmd, stderr);
1633 if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1635 if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1641 if (i < ntok && *tokens[i] == '>') {
1644 if (*(tokens[i]+1) != '\0')
1645 fname = tokens[i]+1;
1646 else if ((i+1) < ntok)
1647 fname = tokens[i+1];
1649 (void) fprintf(stderr, "***No file for redirect\n");
1653 current_output = fopen(fname, "w");
1654 if (current_output == NULL) {
1655 (void) fprintf(stderr, "***Error opening %s: ", fname);
1659 i = 1; /* flag we need a close */
1661 current_output = stdout;
1662 i = 0; /* flag no close */
1665 if (interactive && setjmp(interrupt_buf)) {
1670 (xcmd->handler)(&pcmd, current_output);
1671 jump = 0; /* HMS: 961106: was after fclose() */
1672 if (i) (void) fclose(current_output);
1680 * tokenize - turn a command line into tokens
1682 * SK: Modified to allow a quoted string
1684 * HMS: If the first character of the first token is a ':' then (after
1685 * eating inter-token whitespace) the 2nd token is the rest of the line.
1695 register const char *cp;
1697 static char tspace[MAXLINE];
1701 for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1704 /* Skip inter-token whitespace */
1705 while (ISSPACE(*cp))
1708 /* If we're at EOL we're done */
1712 /* If this is the 2nd token and the first token begins
1713 * with a ':', then just grab to EOL.
1716 if (*ntok == 1 && tokens[0][0] == ':') {
1718 if (sp - tspace >= MAXLINE)
1721 } while (!ISEOL(*cp));
1724 /* Check if this token begins with a double quote.
1725 * If yes, continue reading till the next double quote
1727 else if (*cp == '\"') {
1730 if (sp - tspace >= MAXLINE)
1733 } while ((*cp != '\"') && !ISEOL(*cp));
1734 /* HMS: a missing closing " should be an error */
1738 if (sp - tspace >= MAXLINE)
1741 } while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1742 /* HMS: Why check for a " in the previous line? */
1745 if (sp - tspace >= MAXLINE)
1754 "***Line `%s' is too big\n",
1761 * getarg - interpret an argument token
1772 switch (code & ~OPT) {
1778 if (!getnetnum(str, &argp->netnum, NULL, 0))
1783 if ('&' == str[0]) {
1784 if (!atouint(&str[1], &ul)) {
1786 "***Association index `%s' invalid/undecodable\n",
1790 if (0 == numassoc) {
1792 if (0 == numassoc) {
1794 "***No associations found, `%s' unknown\n",
1799 ul = min(ul, numassoc);
1800 argp->uval = assoc_cache[ul - 1].assid;
1803 if (!atouint(str, &argp->uval)) {
1804 fprintf(stderr, "***Illegal unsigned value %s\n",
1811 if (!atoint(str, &argp->ival)) {
1812 fprintf(stderr, "***Illegal integer value %s\n",
1819 if (!strcmp("-6", str)) {
1821 } else if (!strcmp("-4", str)) {
1824 fprintf(stderr, "***Version must be either 4 or 6\n");
1832 #endif /* !BUILD_AS_LIB */
1836 * findcmd - find a command in a command description table
1841 struct xcmd * clist1,
1842 struct xcmd * clist2,
1849 struct xcmd *nearmatch = NULL;
1856 else if (clist2 != 0)
1862 for (cl = clist; cl->keyword != 0; cl++) {
1863 /* do a first character check, for efficiency */
1864 if (*str != *(cl->keyword))
1866 if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1868 * Could be extact match, could be approximate.
1869 * Is exact if the length of the keyword is the
1872 if (*((cl->keyword) + clen) == '\0') {
1882 * See if there is more to do. If so, go again. Sorry about the
1883 * goto, too much looking at BSD sources...
1885 if (clist == clist1 && clist2 != 0) {
1891 * If we got extactly 1 near match, use it, else return number
1903 * getnetnum - given a host name, return its net number
1904 * and (optional) full name
1914 struct addrinfo hints, *ai = NULL;
1917 hints.ai_flags = AI_CANONNAME;
1918 #ifdef AI_ADDRCONFIG
1919 hints.ai_flags |= AI_ADDRCONFIG;
1923 * decodenetnum only works with addresses, but handles syntax
1924 * that getaddrinfo doesn't: [2001::1]:1234
1926 if (decodenetnum(hname, num)) {
1927 if (fullhost != NULL)
1928 getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1929 LENHOSTNAME, NULL, 0, 0);
1931 } else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1932 INSIST(sizeof(*num) >= ai->ai_addrlen);
1933 memcpy(num, ai->ai_addr, ai->ai_addrlen);
1934 if (fullhost != NULL) {
1935 if (ai->ai_canonname != NULL)
1936 strlcpy(fullhost, ai->ai_canonname,
1939 getnameinfo(&num->sa, SOCKLEN(num),
1940 fullhost, LENHOSTNAME, NULL,
1946 fprintf(stderr, "***Can't find host %s\n", hname);
1953 * nntohost - convert network number to host name. This routine enforces
1954 * the showhostnames setting.
1961 return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
1966 * nntohost_col - convert network number to host name in fixed width.
1967 * This routine enforces the showhostnames setting.
1968 * When displaying hostnames longer than the width,
1969 * the first part of the hostname is displayed. When
1970 * displaying numeric addresses longer than the width,
1971 * Such as IPv6 addresses, the caller decides whether
1972 * the first or last of the numeric address is used.
1978 int preserve_lowaddrbits
1983 if (!showhostnames || SOCK_UNSPEC(addr)) {
1984 if (preserve_lowaddrbits)
1985 out = trunc_left(stoa(addr), width);
1987 out = trunc_right(stoa(addr), width);
1988 } else if (ISREFCLOCKADR(addr)) {
1989 out = refnumtoa(addr);
1991 out = trunc_right(socktohost(addr), width);
1998 * nntohostp() is the same as nntohost() plus a :port suffix
2008 if (!showhostnames || SOCK_UNSPEC(netnum))
2009 return sptoa(netnum);
2010 else if (ISREFCLOCKADR(netnum))
2011 return refnumtoa(netnum);
2013 hostn = socktohost(netnum);
2015 snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
2021 * rtdatetolfp - decode an RT-11 date into an l_fp
2031 struct calendar cal;
2037 * An RT-11 date looks like:
2039 * d[d]-Mth-y[y] hh:mm:ss
2041 * (No docs, but assume 4-digit years are also legal...)
2043 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
2046 if (!isdigit(pgetc(cp))) {
2049 * Catch special case
2057 cal.monthday = (u_char) (*cp++ - '0'); /* ascii dependent */
2058 if (isdigit(pgetc(cp))) {
2059 cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
2060 cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
2066 for (i = 0; i < 3; i++)
2070 for (i = 0; i < 12; i++)
2071 if (STREQ(buf, months[i]))
2075 cal.month = (u_char)(i + 1);
2080 if (!isdigit(pgetc(cp)))
2082 cal.year = (u_short)(*cp++ - '0');
2083 if (isdigit(pgetc(cp))) {
2084 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2085 cal.year = (u_short)(*cp++ - '0');
2087 if (isdigit(pgetc(cp))) {
2088 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2089 cal.year = (u_short)(cal.year + *cp++ - '0');
2091 if (isdigit(pgetc(cp))) {
2092 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2093 cal.year = (u_short)(cal.year + *cp++ - '0');
2097 * Catch special case. If cal.year == 0 this is a zero timestamp.
2099 if (cal.year == 0) {
2104 if (*cp++ != ' ' || !isdigit(pgetc(cp)))
2106 cal.hour = (u_char)(*cp++ - '0');
2107 if (isdigit(pgetc(cp))) {
2108 cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
2109 cal.hour = (u_char)(cal.hour + *cp++ - '0');
2112 if (*cp++ != ':' || !isdigit(pgetc(cp)))
2114 cal.minute = (u_char)(*cp++ - '0');
2115 if (isdigit(pgetc(cp))) {
2116 cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
2117 cal.minute = (u_char)(cal.minute + *cp++ - '0');
2120 if (*cp++ != ':' || !isdigit(pgetc(cp)))
2122 cal.second = (u_char)(*cp++ - '0');
2123 if (isdigit(pgetc(cp))) {
2124 cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
2125 cal.second = (u_char)(cal.second + *cp++ - '0');
2129 * For RT-11, 1972 seems to be the pivot year
2136 lfp->l_ui = caltontp(&cal);
2143 * decodets - decode a timestamp into an l_fp format number, with
2144 * consideration of fuzzball formats.
2157 * If it starts with a 0x, decode as hex.
2159 if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2160 return hextolfp(str+2, lfp);
2163 * If it starts with a '"', try it as an RT-11 date.
2168 while ('"' != *cp && '\0' != *cp &&
2169 b < COUNTOF(buf) - 1)
2172 return rtdatetolfp(buf, lfp);
2176 * Might still be hex. Check out the first character. Talk
2179 if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2180 return hextolfp(str, lfp);
2183 * Try it as a decimal. If this fails, try as an unquoted
2184 * RT-11 date. This code should go away eventually.
2186 if (atolfp(str, lfp))
2189 return rtdatetolfp(str, lfp);
2194 * decodetime - decode a time value. It should be in milliseconds
2202 return mstolfp(str, lfp);
2207 * decodeint - decode an integer
2216 if (*(str+1) == 'x' || *(str+1) == 'X')
2217 return hextoint(str+2, (u_long *)val);
2218 return octtoint(str, (u_long *)val);
2220 return atoint(str, val);
2225 * decodeuint - decode an unsigned integer
2234 if (*(str + 1) == 'x' || *(str + 1) == 'X')
2235 return (hextoint(str + 2, val));
2236 return (octtoint(str, val));
2238 return (atouint(str, val));
2243 * decodearr - decode an array of time values
2258 while (*narr < amax && *cp) {
2259 if (isspace(pgetc(cp))) {
2262 while (*cp && isspace(pgetc(cp)));
2266 if (bp != (buf + sizeof(buf) - 1))
2269 } while (*cp && !isspace(pgetc(cp)));
2272 if (!decodetime(buf, lfpa))
2283 * Finally, the built in command handlers
2287 * help - tell about commands, or details of a particular command
2295 struct xcmd *xcp = NULL; /* quiet warning */
2297 const char *list[100];
2303 if (pcmd->nargs == 0) {
2305 for (xcp = builtins; xcp->keyword != NULL; xcp++) {
2306 if (*(xcp->keyword) != '?' &&
2307 words < COUNTOF(list))
2308 list[words++] = xcp->keyword;
2310 for (xcp = opcmds; xcp->keyword != NULL; xcp++)
2311 if (words < COUNTOF(list))
2312 list[words++] = xcp->keyword;
2314 qsort((void *)list, words, sizeof(list[0]), helpsort);
2316 for (word = 0; word < words; word++) {
2317 length = strlen(list[word]);
2318 col = max(col, length);
2321 cols = SCREENWIDTH / ++col;
2322 rows = (words + cols - 1) / cols;
2324 fprintf(fp, "ntpq commands:\n");
2326 for (row = 0; row < rows; row++) {
2327 for (word = row; word < words; word += rows)
2328 fprintf(fp, "%-*.*s", (int)col,
2329 (int)col - 1, list[word]);
2333 cmd = pcmd->argval[0].string;
2334 words = findcmd(cmd, builtins, opcmds, &xcp);
2337 "Command `%s' is unknown\n", cmd);
2339 } else if (words >= 2) {
2341 "Command `%s' is ambiguous\n", cmd);
2344 fprintf(fp, "function: %s\n", xcp->comment);
2345 printusage(xcp, fp);
2351 * helpsort - do hostname qsort comparisons
2359 const char * const * name1 = t1;
2360 const char * const * name2 = t2;
2362 return strcmp(*name1, *name2);
2367 * printusage - print usage information for a command
2377 /* XXX: Do we need to warn about extra args here too? */
2379 (void) fprintf(fp, "usage: %s", xcp->keyword);
2380 for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2381 if (xcp->arg[i] & OPT)
2382 (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2384 (void) fprintf(fp, " %s", xcp->desc[i]);
2386 (void) fprintf(fp, "\n");
2391 * timeout - set time out time
2401 if (pcmd->nargs == 0) {
2402 val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2403 (void) fprintf(fp, "primary timeout %d ms\n", val);
2405 tvout.tv_sec = pcmd->argval[0].uval / 1000;
2406 tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
2413 * auth_delay - set delay for auth requests
2424 if (pcmd->nargs == 0) {
2425 val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2426 (void) fprintf(fp, "delay %lu ms\n", val);
2428 if (pcmd->argval[0].ival < 0) {
2430 val = (u_long)(-pcmd->argval[0].ival);
2433 val = (u_long)pcmd->argval[0].ival;
2436 delay_time.l_ui = val / 1000;
2438 delay_time.l_uf = val * 4294967; /* 2**32/1000 */
2447 * host - set the host we are dealing with.
2457 if (pcmd->nargs == 0) {
2459 (void) fprintf(fp, "current host is %s\n",
2462 (void) fprintf(fp, "no current host\n");
2467 ai_fam_templ = ai_fam_default;
2468 if (pcmd->nargs == 2) {
2469 if (!strcmp("-4", pcmd->argval[i].string))
2470 ai_fam_templ = AF_INET;
2471 else if (!strcmp("-6", pcmd->argval[i].string))
2472 ai_fam_templ = AF_INET6;
2477 if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
2478 fprintf(fp, "current host set to %s\n", currenthost);
2482 fprintf(fp, "current host remains %s\n",
2485 fprintf(fp, "still no current host\n");
2491 * poll - do one (or more) polls of the host via NTP
2500 (void) fprintf(fp, "poll not implemented yet\n");
2505 * showdrefid2str - return a string explanation of the value of drefid
2508 showdrefid2str(void)
2522 * drefid - display/change "display hash"
2530 if (pcmd->nargs == 0) {
2531 (void) fprintf(fp, "drefid value is %s\n", showdrefid2str());
2533 } else if (STREQ(pcmd->argval[0].string, "hash")) {
2534 drefid = REFID_HASH;
2535 } else if (STREQ(pcmd->argval[0].string, "ipv4")) {
2536 drefid = REFID_IPV4;
2538 (void) fprintf(fp, "What?\n");
2541 (void) fprintf(fp, "drefid value set to %s\n", showdrefid2str());
2546 * keyid - get a keyid to use for authenticating requests
2554 if (pcmd->nargs == 0) {
2555 if (info_auth_keyid == 0)
2556 (void) fprintf(fp, "no keyid defined\n");
2558 (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
2560 /* allow zero so that keyid can be cleared. */
2561 if(pcmd->argval[0].uval > NTP_MAXKEY)
2562 (void) fprintf(fp, "Invalid key identifier\n");
2563 info_auth_keyid = pcmd->argval[0].uval;
2568 * keytype - get type of key to use for authenticating requests
2576 const char * digest_name;
2581 fprintf(fp, "keytype is %s with %lu octet digests\n",
2582 keytype_name(info_auth_keytype),
2583 (u_long)info_auth_hashlen);
2587 digest_name = pcmd->argval[0].string;
2589 key_type = keytype_from_text(digest_name, &digest_len);
2592 fprintf(fp, "keytype is not valid. "
2594 "Type \"help keytype\" for the available digest types.\n");
2596 "Only \"md5\" is available.\n");
2601 info_auth_keytype = key_type;
2602 info_auth_hashlen = digest_len;
2607 * passwd - get an authentication key
2618 if (info_auth_keyid == 0) {
2619 info_auth_keyid = getkeyid("Keyid: ");
2620 if (info_auth_keyid == 0) {
2621 (void)fprintf(fp, "Keyid must be defined\n");
2625 if (pcmd->nargs >= 1)
2626 pass = pcmd->argval[0].string;
2628 pass = getpass_keytype(info_auth_keytype);
2629 if ('\0' == pass[0]) {
2630 fprintf(fp, "Password unchanged\n");
2634 authusekey(info_auth_keyid, info_auth_keytype,
2635 (const u_char *)pass);
2636 authtrust(info_auth_keyid, 1);
2641 * hostnames - set the showhostnames flag
2649 if (pcmd->nargs == 0) {
2651 (void) fprintf(fp, "hostnames being shown\n");
2653 (void) fprintf(fp, "hostnames not being shown\n");
2655 if (STREQ(pcmd->argval[0].string, "yes"))
2657 else if (STREQ(pcmd->argval[0].string, "no"))
2660 (void)fprintf(stderr, "What?\n");
2667 * setdebug - set/change debugging level
2675 if (pcmd->nargs == 0) {
2676 (void) fprintf(fp, "debug level is %d\n", debug);
2678 } else if (STREQ(pcmd->argval[0].string, "no")) {
2680 } else if (STREQ(pcmd->argval[0].string, "more")) {
2682 } else if (STREQ(pcmd->argval[0].string, "less")) {
2685 (void) fprintf(fp, "What?\n");
2688 (void) fprintf(fp, "debug level set to %d\n", debug);
2693 * quit - stop this nonsense
2703 closesocket(sockfd); /* cleanliness next to godliness */
2709 * version - print the current version number
2719 (void) fprintf(fp, "%s\n", Version);
2725 * raw - set raw mode output
2735 (void) fprintf(fp, "Output set to raw\n");
2740 * cooked - set cooked mode output
2750 (void) fprintf(fp, "Output set to cooked\n");
2756 * authenticate - always authenticate requests to this host
2764 if (pcmd->nargs == 0) {
2767 "authenticated requests being sent\n");
2770 "unauthenticated requests being sent\n");
2772 if (STREQ(pcmd->argval[0].string, "yes")) {
2774 } else if (STREQ(pcmd->argval[0].string, "no")) {
2777 (void)fprintf(stderr, "What?\n");
2783 * ntpversion - choose the NTP version to use
2791 if (pcmd->nargs == 0) {
2793 "NTP version being claimed is %d\n", pktversion);
2795 if (pcmd->argval[0].uval < NTP_OLDVERSION
2796 || pcmd->argval[0].uval > NTP_VERSION) {
2797 (void) fprintf(stderr, "versions %d to %d, please\n",
2798 NTP_OLDVERSION, NTP_VERSION);
2800 pktversion = (u_char) pcmd->argval[0].uval;
2806 static void __attribute__((__format__(__printf__, 1, 0)))
2807 vwarning(const char *fmt, va_list ap)
2810 (void) fprintf(stderr, "%s: ", progname);
2811 vfprintf(stderr, fmt, ap);
2812 (void) fprintf(stderr, ": %s\n", strerror(serrno));
2816 * warning - print a warning message
2818 static void __attribute__((__format__(__printf__, 1, 2)))
2832 * error - print a message and exit
2834 static void __attribute__((__format__(__printf__, 1, 2)))
2847 * getkeyid - prompt the user for a keyid to use
2851 const char *keyprompt
2861 if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
2863 if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
2864 #endif /* SYS_WINNT */
2867 setbuf(fi, (char *)NULL);
2868 fprintf(stderr, "%s", keyprompt); fflush(stderr);
2869 for (i = 0, ilim = COUNTOF(pbuf) - 1;
2870 i < ilim && (c = getc(fi)) != '\n' && c != EOF;
2872 pbuf[i++] = (char)c;
2877 return (u_long) atoi(pbuf);
2882 * atoascii - printable-ize possibly ascii data using the character
2883 * transformations cat -v uses.
2893 const u_char * pchIn;
2894 const u_char * pchInLimit;
2898 pchIn = (const u_char *)in;
2899 pchInLimit = pchIn + in_octets;
2900 pchOut = (u_char *)out;
2902 if (NULL == pchIn) {
2910 if (0 == --out_octets) { \
2917 for ( ; pchIn < pchInLimit; pchIn++) {
2928 ONEOUT((u_char)(c + '@'));
2929 } else if (0x7f == c) {
2942 * makeascii - print possibly ascii data using the character
2943 * transformations that cat -v uses.
2952 const u_char *data_u_char;
2956 data_u_char = (const u_char *)data;
2958 for (cp = data_u_char; cp < data_u_char + length; cp++) {
2969 } else if (0x7f == c) {
2979 * asciize - same thing as makeascii except add a newline
2988 makeascii(length, data, fp);
2994 * truncate string to fit clipping excess at end.
2995 * "too long" -> "too l"
2996 * Used for hostnames.
3009 if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
3011 memcpy(out, src, width);
3022 * truncate string to fit by preserving right side and using '_' to hint
3023 * "too long" -> "_long"
3024 * Used for local IPv6 addresses, where low bits differentiate.
3037 if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
3040 memcpy(&out[1], &src[sl + 1 - width], width);
3050 * Some circular buffer space
3055 char circ_buf[NUMCB][CBLEN];
3059 * nextvar - find the next variable in the buffer
3074 static char name[MAXVARLEN];
3075 static char value[MAXVALLEN];
3078 cpend = cp + *datalen;
3081 * Space past commas and white space
3083 while (cp < cpend && (*cp == ',' || isspace(pgetc(cp))))
3089 * Copy name until we hit a ',', an '=', a '\r' or a '\n'. Backspace
3090 * over any white space and terminate it.
3092 srclen = strcspn(cp, ",=\r\n");
3093 srclen = min(srclen, (size_t)(cpend - cp));
3095 while (len > 0 && isspace(pgetc(&cp[len - 1])))
3097 if (len >= sizeof(name))
3100 memcpy(name, cp, len);
3106 * Check if we hit the end of the buffer or a ','. If so we are done.
3108 if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
3112 *datalen = size2int_sat(cpend - cp);
3118 * So far, so good. Copy out the value
3120 cp++; /* past '=' */
3121 while (cp < cpend && (isspace(pgetc(cp)) && *cp != '\r' && *cp != '\n'))
3127 } while (np < cpend && '"' != *np);
3128 if (np < cpend && '"' == *np)
3131 while (np < cpend && ',' != *np && '\r' != *np)
3135 if (np > cpend || len >= sizeof(value) ||
3136 (np < cpend && ',' != *np && '\r' != *np))
3138 memcpy(value, cp, len);
3140 * Trim off any trailing whitespace
3142 while (len > 0 && isspace(pgetc(&value[len - 1])))
3147 * Return this. All done.
3149 if (np < cpend && ',' == *np)
3152 *datalen = size2int_sat(cpend - np);
3159 varfmt(const char * varname)
3163 for (n = 0; n < COUNTOF(cookedvars); n++)
3164 if (!strcmp(varname, cookedvars[n].varname))
3165 return cookedvars[n].fmt;
3172 * printvars - print variables returned in response packet
3185 rawprint(sttype, length, data, status, quiet, fp);
3187 cookedprint(sttype, length, data, status, quiet, fp);
3192 * rawprint - do a printout of the data in raw mode
3208 * Essentially print the data as is. We reformat unprintables, though.
3211 cpend = data + length;
3214 (void) fprintf(fp, "status=0x%04x,\n", status);
3216 while (cp < cpend) {
3219 * If this is a \r and the next character is a
3220 * \n, supress this, else pretty print it. Otherwise
3221 * just output the character.
3223 if (cp == (cpend - 1) || *(cp + 1) != '\n')
3224 makeascii(1, cp, fp);
3225 } else if (isspace(pgetc(cp)) || isprint(pgetc(cp)))
3228 makeascii(1, cp, fp);
3235 * Global data used by the cooked output routines
3237 int out_chars; /* number of characters output */
3238 int out_linecount; /* number of characters output on this line */
3242 * startoutput - get ready to do cooked output
3253 * output - output a variable=value combination
3264 /* strlen of "name=value" */
3265 len = size2int_sat(strlen(name) + 1 + strlen(value));
3267 if (out_chars != 0) {
3269 if ((out_linecount + len + 2) > MAXOUTLINE) {
3282 out_linecount += len;
3287 * endoutput - terminate a block of cooked output
3300 * outputarr - output an array of values
3318 * Hack to align delay and offset values
3320 for (i = (int)strlen(name); i < 11; i++)
3323 for (i = narr; i > 0; i--) {
3326 cp = lfptoms(lfp, 2);
3341 output(fp, name, buf);
3349 register char *cp, *s;
3352 register const char *sep;
3355 s = cp = circ_buf[nextcb];
3356 if (++nextcb >= NUMCB)
3358 cb = sizeof(circ_buf[0]);
3360 snprintf(cp, cb, "%02lx", val);
3364 strlcat(cp, " ok", cb);
3372 for (i = 0; i < (int)COUNTOF(tstflagnames); i++) {
3374 snprintf(cp, cb, "%s%s", sep,
3390 * cookedprint - output variables in cooked mode
3413 char bn[2 * MAXVARLEN];
3414 char bv[2 * MAXVALLEN];
3416 UNUSED_ARG(datatype);
3419 fprintf(fp, "status=%04x %s,\n", status,
3420 statustoa(datatype, status));
3423 while (nextvar(&length, &data, &name, &value)) {
3433 if (!value || !decodets(value, &lfp))
3436 output(fp, name, prettydate(&lfp));
3439 case HA: /* fallthru */
3441 if (!value || !decodenetnum(value, &hval)) {
3443 } else if (fmt == HA){
3444 output(fp, name, nntohost(&hval));
3446 output(fp, name, stoa(&hval));
3453 } else if (decodenetnum(value, &hval)) {
3454 if (ISREFCLOCKADR(&hval))
3458 output(fp, name, stoa(&hval));
3459 } else if (strlen(value) <= 4) {
3460 output(fp, name, value);
3467 if (!value || !decodeuint(value, &uval) || uval > 3) {
3477 output(fp, name, b);
3482 if (!value || !decodeuint(value, &uval)) {
3485 snprintf(b, sizeof(b), "%03lo", uval);
3486 output(fp, name, b);
3491 if (!value || !decodearr(value, &narr, lfparr, 8))
3494 outputarr(fp, name, narr, lfparr);
3498 if (!value || !decodeuint(value, &uval))
3501 output(fp, name, tstflags(uval));
3505 fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3511 if (output_raw != 0) {
3512 /* TALOS-CAN-0063: avoid buffer overrun */
3513 atoascii(name, MAXVARLEN, bn, sizeof(bn));
3514 if (output_raw != '*') {
3515 atoascii(value, MAXVALLEN,
3516 bv, sizeof(bv) - 1);
3518 bv[len] = output_raw;
3521 atoascii(value, MAXVALLEN,
3532 * sortassoc - sort associations in the cache into ascending order
3538 qsort(assoc_cache, (size_t)numassoc,
3539 sizeof(assoc_cache[0]), &assoccmp);
3544 * assoccmp - compare two associations
3552 const struct association *ass1 = t1;
3553 const struct association *ass2 = t2;
3555 if (ass1->assid < ass2->assid)
3557 if (ass1->assid > ass2->assid)
3564 * grow_assoc_cache() - enlarge dynamic assoc_cache array
3566 * The strategy is to add an assumed 4k page size at a time, leaving
3567 * room for malloc() bookkeeping overhead equivalent to 4 pointers.
3570 grow_assoc_cache(void)
3572 static size_t prior_sz;
3575 new_sz = prior_sz + 4 * 1024;
3576 if (0 == prior_sz) {
3577 new_sz -= 4 * sizeof(void *);
3579 assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
3581 assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
3586 * ntpq_custom_opt_handler - autoopts handler for -c and -p
3588 * By default, autoopts loses the relative order of -c and -p options
3589 * on the command line. This routine replaces the default handler for
3590 * those routines and builds a list of commands to execute preserving
3594 ntpq_custom_opt_handler(
3599 switch (pOptDesc->optValue) {
3603 "ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3604 pOptDesc->optValue, pOptDesc->optValue);
3608 ADDCMD(pOptDesc->pzLastArg);
3617 * Obtain list of digest names
3620 #if defined(OPENSSL) && !defined(HAVE_EVP_MD_DO_ALL_SORTED)
3621 # if defined(_MSC_VER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
3622 # define HAVE_EVP_MD_DO_ALL_SORTED
3627 # ifdef HAVE_EVP_MD_DO_ALL_SORTED
3628 # define K_PER_LINE 8
3629 # define K_NL_PFX_STR "\n "
3630 # define K_DELIM_STR ", "
3640 list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg)
3642 size_t len, n, digest_len;
3643 const char *name, **seen;
3644 struct hstate *hstate = arg;
3647 /* m is MD obj, from is name or alias, to is base name for alias */
3648 if (!m || !from || to) {
3649 return; /* Ignore aliases */
3652 /* Discard MACs that NTP won't accept. */
3653 /* Keep this consistent with keytype_from_text() in ssl_init.c. */
3654 if (EVP_MD_size(m) > (MAX_MAC_LEN - sizeof(keyid_t))) {
3658 name = EVP_MD_name(m);
3660 /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
3662 for (cp = name; *cp; cp++) {
3663 if (islower((unsigned char)*cp)) {
3668 len = (cp - name) + 1;
3670 /* There are duplicates. Discard if name has been seen. */
3672 for (seen = hstate->seen; *seen; seen++) {
3673 if (!strcmp(*seen, name)) {
3678 n = (seen - hstate->seen) + 2;
3679 hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
3680 hstate->seen[n-2] = name;
3681 hstate->seen[n-1] = NULL;
3683 if (hstate->list != NULL) {
3684 len += strlen(hstate->list);
3687 len += (hstate->idx >= K_PER_LINE)
3688 ? strlen(K_NL_PFX_STR)
3689 : strlen(K_DELIM_STR);
3691 if (hstate->list == NULL) {
3692 hstate->list = (char *)emalloc(len);
3693 hstate->list[0] = '\0';
3695 hstate->list = (char *)erealloc(hstate->list, len);
3698 sprintf(hstate->list + strlen(hstate->list), "%s%s",
3699 ((hstate->idx >= K_PER_LINE) ? K_NL_PFX_STR : K_DELIM_STR),
3702 if (hstate->idx >= K_PER_LINE) {
3710 /* Insert CMAC into SSL digests list */
3712 insert_cmac(char *list)
3718 /* If list empty, we need to insert CMAC on new line */
3719 insert = (!list || !*list);
3722 len = strlen(K_NL_PFX_STR) + strlen(CMAC);
3723 list = (char *)erealloc(list, len + 1);
3724 sprintf(list, "%s%s", K_NL_PFX_STR, CMAC);
3725 } else { /* List not empty */
3726 /* Check if CMAC already in list - future proofing */
3727 const char *cmac_sn;
3730 cmac_sn = OBJ_nid2sn(NID_cmac);
3732 insert = cmac_sn != NULL && *cmac_sn != '\0';
3734 /* CMAC in list if found, followed by nul char or ',' */
3735 while (insert && NULL != (cmac_p = strstr(cmac_p, cmac_sn))) {
3736 cmac_p += strlen(cmac_sn);
3737 /* Still need to insert if not nul and not ',' */
3738 insert = *cmac_p && ',' != *cmac_p;
3741 /* Find proper insertion point */
3748 /* Default to start if list empty */
3753 /* While new lines */
3754 while (delim < list + len && *delim &&
3755 !strncmp(K_NL_PFX_STR, delim, strlen(K_NL_PFX_STR))) {
3756 point = delim + strlen(K_NL_PFX_STR);
3758 /* While digest names on line */
3759 while (point < list + len && *point) {
3760 /* Another digest after on same or next line? */
3761 delim = strstr( point, K_DELIM_STR);
3762 last_nl = strstr( point, K_NL_PFX_STR);
3764 /* No - end of list */
3765 if (!delim && !last_nl) {
3768 /* New line and no delim or before delim? */
3769 if (last_nl && (!delim || last_nl < delim)) {
3773 /* Found insertion point where CMAC before entry? */
3774 if (strncmp(CMAC, point, delim - point) < 0) {
3779 if (delim < list + len && *delim &&
3780 !strncmp(K_DELIM_STR, delim, strlen(K_DELIM_STR))) {
3781 point += strlen(K_DELIM_STR);
3785 } /* While digest names on line */
3786 } /* While new lines */
3788 /* If found in list */
3790 /* insert cmac and delim */
3791 /* Space for list could move - save offset */
3792 ptrdiff_t p_offset = point - list;
3793 len += strlen(CMAC) + strlen(K_DELIM_STR);
3794 list = (char *)erealloc(list, len + 1);
3795 point = list + p_offset;
3796 /* move to handle src/dest overlap */
3797 memmove(point + strlen(CMAC) + strlen(K_DELIM_STR),
3798 point, strlen(point) + 1);
3799 strncpy(point, CMAC, strlen(CMAC));
3800 strncpy(point + strlen(CMAC), K_DELIM_STR, strlen(K_DELIM_STR));
3801 } else { /* End of list */
3802 /* append delim and cmac */
3803 len += strlen(K_DELIM_STR) + strlen(CMAC);
3804 list = (char *)erealloc(list, len + 1);
3805 strcpy(list + strlen(list), K_DELIM_STR);
3806 strcpy(list + strlen(list), CMAC);
3809 } /* List not empty */
3818 list_digest_names(void)
3823 # ifdef HAVE_EVP_MD_DO_ALL_SORTED
3824 struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
3826 /* replace calloc(1, sizeof(const char *)) */
3827 hstate.seen = (const char **)emalloc_zero(sizeof(const char *));
3830 EVP_MD_do_all_sorted(list_md_fn, &hstate);
3834 list = insert_cmac(list); /* Insert CMAC into SSL digests list */
3837 list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
3838 strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
3841 list = (char *)emalloc(sizeof("md5"));
3842 strcpy(list, "md5");
3848 #define CTRLC_STACK_MAX 4
3849 static volatile size_t ctrlc_stack_len = 0;
3850 static volatile Ctrl_C_Handler ctrlc_stack[CTRLC_STACK_MAX];
3855 push_ctrl_c_handler(
3859 size_t size = ctrlc_stack_len;
3860 if (func && (size < CTRLC_STACK_MAX)) {
3861 ctrlc_stack[size] = func;
3862 ctrlc_stack_len = size + 1;
3873 size_t size = ctrlc_stack_len;
3876 if (func == NULL || func == ctrlc_stack[size]) {
3877 ctrlc_stack_len = size;
3887 size_t size = ctrlc_stack_len;
3889 if ((*ctrlc_stack[--size])())
3903 char * buf = emalloc(len);
3906 /* Note: we expect the memory allocation to fail long before the
3907 * increment in buffer size actually overflows.
3909 buf = (buf) ? erealloc(buf, len) : emalloc(len);
3912 prc = vsnprintf(buf, len, fmt, va);
3916 /* might be very old vsnprintf. Or actually MSVC... */
3920 if ((size_t)prc >= len) {
3921 /* at least we have the proper size now... */
3922 len = (size_t)prc + 1;
3925 if ((size_t)prc < (len - 32))
3926 buf = erealloc(buf, (size_t)prc + 1);