summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2023-04-23 19:04:13 -1000
committerKinseyMoore <48726349+KinseyMoore@users.noreply.github.com>2023-05-15 21:45:54 -0500
commit3f071ffe05fc14bf41c2075882fdb5493c4a2e5b (patch)
tree91c6a66c77630b7efcbd2c6aef99bcf9a1b2ddef
parentbsd/ntp: Use msyslog support for %m (diff)
downloadrtems-net-services-3f071ffe05fc14bf41c2075882fdb5493c4a2e5b.tar.bz2
bsd/ntp: Import ntpq
-rwxr-xr-xbsd/freebsd/contrib/ntp/include/ntp_lineedit.h13
-rw-r--r--bsd/freebsd/contrib/ntp/libntp/atolfp.c122
-rwxr-xr-xbsd/freebsd/contrib/ntp/libntp/atouint.c42
-rw-r--r--bsd/freebsd/contrib/ntp/libntp/authusekey.c34
-rw-r--r--bsd/freebsd/contrib/ntp/libntp/caltontp.c68
-rwxr-xr-xbsd/freebsd/contrib/ntp/libntp/hextoint.c42
-rwxr-xr-xbsd/freebsd/contrib/ntp/libntp/hextolfp.c70
-rw-r--r--bsd/freebsd/contrib/ntp/libntp/mstolfp.c99
-rw-r--r--bsd/freebsd/contrib/ntp/libntp/octtoint.c36
-rw-r--r--bsd/freebsd/contrib/ntp/libntp/socktohost.c118
-rw-r--r--bsd/freebsd/contrib/ntp/ntpq/libntpq.c772
-rw-r--r--bsd/freebsd/contrib/ntp/ntpq/libntpq.h109
-rw-r--r--bsd/freebsd/contrib/ntp/ntpq/libntpq_subs.c52
-rw-r--r--bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.c1220
-rw-r--r--bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.h307
-rw-r--r--bsd/freebsd/contrib/ntp/ntpq/ntpq-subs.c4142
-rw-r--r--bsd/freebsd/contrib/ntp/ntpq/ntpq.c4174
-rw-r--r--bsd/freebsd/contrib/ntp/ntpq/ntpq.h162
18 files changed, 11582 insertions, 0 deletions
diff --git a/bsd/freebsd/contrib/ntp/include/ntp_lineedit.h b/bsd/freebsd/contrib/ntp/include/ntp_lineedit.h
new file mode 100755
index 0000000..623c6f4
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/include/ntp_lineedit.h
@@ -0,0 +1,13 @@
+
+/*
+ * ntp_lineedit.h - generic interface to various line editing libs
+ */
+
+int ntp_readline_init(const char *prompt);
+void ntp_readline_uninit(void);
+
+/*
+ * strings returned by ntp_readline go home to free()
+ */
+char * ntp_readline(int *pcount);
+
diff --git a/bsd/freebsd/contrib/ntp/libntp/atolfp.c b/bsd/freebsd/contrib/ntp/libntp/atolfp.c
new file mode 100644
index 0000000..439194e
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/libntp/atolfp.c
@@ -0,0 +1,122 @@
+/*
+ * atolfp - convert an ascii string to an l_fp number
+ */
+#include <config.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_fp.h"
+#include "ntp_string.h"
+#include "ntp_assert.h"
+
+/*
+ * Powers of 10
+ */
+static u_long ten_to_the_n[10] = {
+ 0,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000,
+};
+
+
+int
+atolfp(
+ const char *str,
+ l_fp *lfp
+ )
+{
+ register const char *cp;
+ register u_long dec_i;
+ register u_long dec_f;
+ char *ind;
+ int ndec;
+ int isneg;
+ static const char *digits = "0123456789";
+
+ REQUIRE(str != NULL);
+
+ isneg = 0;
+ dec_i = dec_f = 0;
+ ndec = 0;
+ cp = str;
+
+ /*
+ * We understand numbers of the form:
+ *
+ * [spaces][-|+][digits][.][digits][spaces|\n|\0]
+ */
+ while (isspace((unsigned char)*cp))
+ cp++;
+
+ if (*cp == '-') {
+ cp++;
+ isneg = 1;
+ }
+
+ if (*cp == '+')
+ cp++;
+
+ if (*cp != '.' && !isdigit((unsigned char)*cp))
+ return 0;
+
+ while (*cp != '\0' && (ind = strchr(digits, *cp)) != NULL) {
+ dec_i = (dec_i << 3) + (dec_i << 1); /* multiply by 10 */
+ dec_i += (u_long)(ind - digits);
+ cp++;
+ }
+
+ if (*cp != '\0' && !isspace((unsigned char)*cp)) {
+ if (*cp++ != '.')
+ return 0;
+
+ while (ndec < 9 && *cp != '\0'
+ && (ind = strchr(digits, *cp)) != NULL) {
+ ndec++;
+ dec_f = (dec_f << 3) + (dec_f << 1); /* *10 */
+ dec_f += (u_long)(ind - digits);
+ cp++;
+ }
+
+ while (isdigit((unsigned char)*cp))
+ cp++;
+
+ if (*cp != '\0' && !isspace((unsigned char)*cp))
+ return 0;
+ }
+
+ if (ndec > 0) {
+ register u_long tmp;
+ register u_long bit;
+ register u_long ten_fact;
+
+ ten_fact = ten_to_the_n[ndec];
+
+ tmp = 0;
+ bit = 0x80000000;
+ while (bit != 0) {
+ dec_f <<= 1;
+ if (dec_f >= ten_fact) {
+ tmp |= bit;
+ dec_f -= ten_fact;
+ }
+ bit >>= 1;
+ }
+ if ((dec_f << 1) > ten_fact)
+ tmp++;
+ dec_f = tmp;
+ }
+
+ if (isneg)
+ M_NEG(dec_i, dec_f);
+
+ lfp->l_ui = dec_i;
+ lfp->l_uf = dec_f;
+ return 1;
+}
diff --git a/bsd/freebsd/contrib/ntp/libntp/atouint.c b/bsd/freebsd/contrib/ntp/libntp/atouint.c
new file mode 100755
index 0000000..0a61639
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/libntp/atouint.c
@@ -0,0 +1,42 @@
+#include <config.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "ntp_types.h"
+#include "ntp_stdlib.h"
+
+/*
+ * atouint() - convert an ascii string representing a whole base 10
+ * number to u_long *uval, returning TRUE if successful.
+ * Does not modify *uval and returns FALSE if str is not
+ * a positive base10 integer or is too large for a u_int32.
+ * this function uses u_long but should use u_int32, and
+ * probably be renamed.
+ */
+int
+atouint(
+ const char *str,
+ u_long *uval
+ )
+{
+ u_long u;
+ const char *cp;
+
+ cp = str;
+ if ('\0' == *cp)
+ return 0;
+
+ u = 0;
+ while ('\0' != *cp) {
+ if (!isdigit((unsigned char)*cp))
+ return 0;
+ if (u > 429496729 || (u == 429496729 && *cp >= '6'))
+ return 0; /* overflow */
+ /* hand-optimized u *= 10; */
+ u = (u << 3) + (u << 1);
+ u += *cp++ - '0'; /* not '\0' */
+ }
+
+ *uval = u;
+ return 1;
+}
diff --git a/bsd/freebsd/contrib/ntp/libntp/authusekey.c b/bsd/freebsd/contrib/ntp/libntp/authusekey.c
new file mode 100644
index 0000000..ff449d3
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/libntp/authusekey.c
@@ -0,0 +1,34 @@
+/*
+ * authusekey - decode a key from ascii and use it
+ */
+#include <config.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Types of ascii representations for keys. "Standard" means a 64 bit
+ * hex number in NBS format, i.e. with the low order bit of each byte
+ * a parity bit. "NTP" means a 64 bit key in NTP format, with the
+ * high order bit of each byte a parity bit. "Ascii" means a 1-to-8
+ * character string whose ascii representation is used as the key.
+ */
+int
+authusekey(
+ keyid_t keyno,
+ int keytype,
+ const u_char *str
+ )
+{
+ size_t len;
+
+ len = strlen((const char *)str);
+ if (0 == len)
+ return 0;
+
+ MD5auth_setkey(keyno, keytype, str, len, NULL);
+ return 1;
+}
diff --git a/bsd/freebsd/contrib/ntp/libntp/caltontp.c b/bsd/freebsd/contrib/ntp/libntp/caltontp.c
new file mode 100644
index 0000000..808c94c
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/libntp/caltontp.c
@@ -0,0 +1,68 @@
+/*
+ * caltontp - convert a date to an NTP time
+ */
+#include <config.h>
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+#include "ntp_assert.h"
+#include "ntp_unixtime.h"
+
+/*
+ * Juergen Perlinger, 2008-11-12
+ * Add support for full calendar calculatios. If the day-of-year is provided
+ * (that is, not zero) it will be used instead of month and day-of-month;
+ * otherwise a full turn through the calendar calculations will be taken.
+ *
+ * I know that Harlan Stenn likes to see assertions in production code, and I
+ * agree there, but it would be a tricky thing here. The algorithm is quite
+ * capable of producing sensible answers even to seemingly weird inputs: the
+ * date <any year here>-03-00, the 0.th March of the year, will be automtically
+ * treated as the last day of February, no matter whether the year is a leap
+ * year or not. So adding constraints is merely for the benefit of the callers,
+ * because the only thing we can check for consistency is our input, produced
+ * by somebody else.
+ *
+ * BTW: A total roundtrip using 'caljulian' would be a quite shaky thing:
+ * Because of the truncation of the NTP time stamp to 32 bits and the epoch
+ * unfolding around the current time done by 'caljulian' the roundtrip does
+ * *not* necessarily reproduce the input, especially if the time spec is more
+ * than 68 years off from the current time...
+ */
+
+uint32_t
+caltontp(
+ const struct calendar *jt
+ )
+{
+ int32_t eraday; /* CE Rata Die number */
+ vint64 ntptime;/* resulting NTP time */
+
+ REQUIRE(jt != NULL);
+
+ REQUIRE(jt->month <= 13); /* permit month 0..13! */
+ REQUIRE(jt->monthday <= 32);
+ REQUIRE(jt->yearday <= 366);
+ REQUIRE(jt->hour <= 24);
+ REQUIRE(jt->minute <= MINSPERHR);
+ REQUIRE(jt->second <= SECSPERMIN);
+
+ /*
+ * First convert the date to he corresponding RataDie
+ * number. If yearday is not zero, assume that it contains a
+ * useable value and avoid all calculations involving month
+ * and day-of-month. Do a full evaluation otherwise.
+ */
+ if (jt->yearday)
+ eraday = ntpcal_year_to_ystart(jt->year)
+ + jt->yearday - 1;
+ else
+ eraday = ntpcal_date_to_rd(jt);
+
+ ntptime = ntpcal_dayjoin(eraday - DAY_NTP_STARTS,
+ ntpcal_etime_to_seconds(jt->hour, jt->minute,
+ jt->second));
+ return ntptime.d_s.lo;
+}
diff --git a/bsd/freebsd/contrib/ntp/libntp/hextoint.c b/bsd/freebsd/contrib/ntp/libntp/hextoint.c
new file mode 100755
index 0000000..980a43f
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/libntp/hextoint.c
@@ -0,0 +1,42 @@
+/*
+ * hextoint - convert an ascii string in hex to an unsigned
+ * long, with error checking
+ */
+#include <config.h>
+#include <ctype.h>
+
+#include "ntp_stdlib.h"
+
+int
+hextoint(
+ const char *str,
+ u_long *pu
+ )
+{
+ register u_long u;
+ register const char *cp;
+
+ cp = str;
+
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isxdigit((unsigned char)*cp))
+ return 0;
+ if (u & 0xF0000000)
+ return 0; /* overflow */
+ u <<= 4;
+ if ('0' <= *cp && *cp <= '9')
+ u += *cp++ - '0';
+ else if ('a' <= *cp && *cp <= 'f')
+ u += *cp++ - 'a' + 10;
+ else if ('A' <= *cp && *cp <= 'F')
+ u += *cp++ - 'A' + 10;
+ else
+ return 0;
+ }
+ *pu = u;
+ return 1;
+}
diff --git a/bsd/freebsd/contrib/ntp/libntp/hextolfp.c b/bsd/freebsd/contrib/ntp/libntp/hextolfp.c
new file mode 100755
index 0000000..19a93cd
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/libntp/hextolfp.c
@@ -0,0 +1,70 @@
+/*
+ * hextolfp - convert an ascii hex string to an l_fp number
+ */
+#include <config.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_fp.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+int
+hextolfp(
+ const char *str,
+ l_fp *lfp
+ )
+{
+ register const char *cp;
+ register const char *cpstart;
+ register u_long dec_i;
+ register u_long dec_f;
+ char *ind = NULL;
+ static const char *digits = "0123456789abcdefABCDEF";
+
+ dec_i = dec_f = 0;
+ cp = str;
+
+ /*
+ * We understand numbers of the form:
+ *
+ * [spaces]8_hex_digits[.]8_hex_digits[spaces|\n|\0]
+ */
+ while (isspace((unsigned char)*cp))
+ cp++;
+
+ cpstart = cp;
+ while (*cp != '\0' && (cp - cpstart) < 8 &&
+ (ind = strchr(digits, *cp)) != NULL) {
+ dec_i = dec_i << 4; /* multiply by 16 */
+ dec_i += ((ind - digits) > 15)
+ ? (u_long)(ind - digits - 6)
+ : (u_long)(ind - digits);
+ cp++;
+ }
+
+ if ((cp - cpstart) < 8 || ind == NULL)
+ return 0;
+ if (*cp == '.')
+ cp++;
+
+ cpstart = cp;
+ while (*cp != '\0' && (cp - cpstart) < 8 &&
+ (ind = strchr(digits, *cp)) != NULL) {
+ dec_f = dec_f << 4; /* multiply by 16 */
+ dec_f += ((ind - digits) > 15)
+ ? (u_long)(ind - digits - 6)
+ : (u_long)(ind - digits);
+ cp++;
+ }
+
+ if ((cp - cpstart) < 8 || ind == NULL)
+ return 0;
+
+ if (*cp != '\0' && !isspace((unsigned char)*cp))
+ return 0;
+
+ lfp->l_ui = dec_i;
+ lfp->l_uf = dec_f;
+ return 1;
+}
diff --git a/bsd/freebsd/contrib/ntp/libntp/mstolfp.c b/bsd/freebsd/contrib/ntp/libntp/mstolfp.c
new file mode 100644
index 0000000..3dfc4ef
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/libntp/mstolfp.c
@@ -0,0 +1,99 @@
+/*
+ * mstolfp - convert an ascii string in milliseconds to an l_fp number
+ */
+#include <config.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+int
+mstolfp(
+ const char *str,
+ l_fp *lfp
+ )
+{
+ register const char *cp;
+ register char *bp;
+ register const char *cpdec;
+ char buf[100];
+
+ /*
+ * We understand numbers of the form:
+ *
+ * [spaces][-|+][digits][.][digits][spaces|\n|\0]
+ *
+ * This is one enormous hack. Since I didn't feel like
+ * rewriting the decoding routine for milliseconds, what
+ * is essentially done here is to make a copy of the string
+ * with the decimal moved over three places so the seconds
+ * decoding routine can be used.
+ */
+ bp = buf;
+ cp = str;
+ while (isspace((unsigned char)*cp))
+ cp++;
+
+ if (*cp == '-' || *cp == '+') {
+ *bp++ = *cp++;
+ }
+
+ if (*cp != '.' && !isdigit((unsigned char)*cp))
+ return 0;
+
+
+ /*
+ * Search forward for the decimal point or the end of the string.
+ */
+ cpdec = cp;
+ while (isdigit((unsigned char)*cpdec))
+ cpdec++;
+
+ /*
+ * Found something. If we have more than three digits copy the
+ * excess over, else insert a leading 0.
+ */
+ if ((cpdec - cp) > 3) {
+ do {
+ *bp++ = (char)*cp++;
+ } while ((cpdec - cp) > 3);
+ } else {
+ *bp++ = '0';
+ }
+
+ /*
+ * Stick the decimal in. If we've got less than three digits in
+ * front of the millisecond decimal we insert the appropriate number
+ * of zeros.
+ */
+ *bp++ = '.';
+ if ((cpdec - cp) < 3) {
+ size_t i = 3 - (cpdec - cp);
+ do {
+ *bp++ = '0';
+ } while (--i > 0);
+ }
+
+ /*
+ * Copy the remainder up to the millisecond decimal. If cpdec
+ * is pointing at a decimal point, copy in the trailing number too.
+ */
+ while (cp < cpdec)
+ *bp++ = (char)*cp++;
+
+ if (*cp == '.') {
+ cp++;
+ while (isdigit((unsigned char)*cp))
+ *bp++ = (char)*cp++;
+ }
+ *bp = '\0';
+
+ /*
+ * Check to make sure the string is properly terminated. If
+ * so, give the buffer to the decoding routine.
+ */
+ if (*cp != '\0' && !isspace((unsigned char)*cp))
+ return 0;
+ return atolfp(buf, lfp);
+}
diff --git a/bsd/freebsd/contrib/ntp/libntp/octtoint.c b/bsd/freebsd/contrib/ntp/libntp/octtoint.c
new file mode 100644
index 0000000..e519601
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/libntp/octtoint.c
@@ -0,0 +1,36 @@
+/*
+ * octtoint - convert an ascii string in octal to an unsigned
+ * long, with error checking
+ */
+#include <config.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_stdlib.h"
+
+int
+octtoint(
+ const char *str,
+ u_long *ival
+ )
+{
+ register u_long u;
+ register const char *cp;
+
+ cp = str;
+
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isdigit((unsigned char)*cp) || *cp == '8' || *cp == '9')
+ return 0;
+ if (u >= 0x20000000)
+ return 0; /* overflow */
+ u <<= 3;
+ u += *cp++ - '0'; /* ascii dependent */
+ }
+ *ival = u;
+ return 1;
+}
diff --git a/bsd/freebsd/contrib/ntp/libntp/socktohost.c b/bsd/freebsd/contrib/ntp/libntp/socktohost.c
new file mode 100644
index 0000000..fdf9adb
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/libntp/socktohost.c
@@ -0,0 +1,118 @@
+/*
+ * socktoa - return a numeric host name from a sockaddr_storage structure
+ */
+#include <config.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+#include "ntp.h"
+#include "ntp_debug.h"
+
+
+const char *
+socktohost(
+ const sockaddr_u *sock
+ )
+{
+ const char svc[] = "ntp";
+ char * pbuf;
+ char * pliar;
+ int gni_flags;
+ struct addrinfo hints;
+ struct addrinfo * alist;
+ struct addrinfo * ai;
+ sockaddr_u addr;
+ size_t octets;
+ int a_info;
+ int saved_errno;
+
+ saved_errno = socket_errno();
+
+ /* reverse the address to purported DNS name */
+ LIB_GETBUF(pbuf);
+ gni_flags = NI_DGRAM | NI_NAMEREQD;
+ if (getnameinfo(&sock->sa, SOCKLEN(sock), pbuf, LIB_BUFLENGTH,
+ NULL, 0, gni_flags)) {
+ errno = saved_errno;
+ return stoa(sock); /* use address */
+ }
+
+ TRACE(1, ("%s reversed to %s\n", stoa(sock), pbuf));
+
+ /*
+ * Resolve the reversed name and make sure the reversed address
+ * is among the results.
+ */
+ ZERO(hints);
+ hints.ai_family = AF(sock);
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+ alist = NULL;
+
+ a_info = getaddrinfo(pbuf, svc, &hints, &alist);
+ if (a_info == EAI_NONAME
+#ifdef EAI_NODATA
+ || a_info == EAI_NODATA
+#endif
+ ) {
+ hints.ai_flags = AI_CANONNAME;
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ a_info = getaddrinfo(pbuf, svc, &hints, &alist);
+ }
+#ifdef AI_ADDRCONFIG
+ /* Some older implementations don't like AI_ADDRCONFIG. */
+ if (a_info == EAI_BADFLAGS) {
+ hints.ai_flags &= ~AI_ADDRCONFIG;
+ a_info = getaddrinfo(pbuf, svc, &hints, &alist);
+ }
+#endif
+ if (a_info)
+ goto forward_fail;
+
+ INSIST(alist != NULL);
+
+ for (ai = alist; ai != NULL; ai = ai->ai_next) {
+ /*
+ * Make a convenience sockaddr_u copy from ai->ai_addr
+ * because casting from sockaddr * to sockaddr_u * is
+ * risking alignment problems on platforms where
+ * sockaddr_u has stricter alignment than sockaddr,
+ * such as sparc.
+ */
+ ZERO_SOCK(&addr);
+ octets = min(sizeof(addr), ai->ai_addrlen);
+ memcpy(&addr, ai->ai_addr, octets);
+ if (SOCK_EQ(sock, &addr))
+ break;
+ }
+ freeaddrinfo(alist);
+
+ if (ai != NULL) {
+ errno = saved_errno;
+ return pbuf; /* forward check passed */
+ }
+
+ forward_fail:
+ TRACE(1, ("%s forward check lookup fail: %s\n", pbuf,
+ gai_strerror(a_info)));
+ LIB_GETBUF(pliar);
+ snprintf(pliar, LIB_BUFLENGTH, "%s (%s)", stoa(sock), pbuf);
+
+ errno = saved_errno;
+ return pliar;
+}
diff --git a/bsd/freebsd/contrib/ntp/ntpq/libntpq.c b/bsd/freebsd/contrib/ntp/ntpq/libntpq.c
new file mode 100644
index 0000000..e7f0266
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/ntpq/libntpq.c
@@ -0,0 +1,772 @@
+/*****************************************************************************
+ *
+ * libntpq.c
+ *
+ * This is the wrapper library for ntpq, the NTP query utility.
+ * This library reuses the sourcecode from ntpq and exports a number
+ * of useful functions in a library that can be linked against applications
+ * that need to query the status of a running ntpd. The whole
+ * communcation is based on mode 6 packets.
+ *
+ ****************************************************************************/
+#define LIBNTPQ_C
+#define NO_MAIN_ALLOWED 1
+/* #define BUILD_AS_LIB Already provided by the Makefile */
+
+#include "ntpq.c"
+#include "libntpq.h"
+
+/* Function Prototypes */
+
+
+const char *Version = "libntpq 0.3beta";
+
+/* global variables used for holding snapshots of data */
+char peervars[NTPQ_BUFLEN];
+int peervarlen = 0;
+associd_t peervar_assoc = 0;
+char clockvars[NTPQ_BUFLEN];
+int clockvarlen = 0;
+int clockvar_assoc = 0;
+char sysvars[NTPQ_BUFLEN];
+int sysvarlen = 0;
+char *ntpq_resultbuffer[NTPQ_BUFLEN];
+unsigned short ntpq_associations[MAXASSOC];
+struct ntpq_varlist ntpq_varlist[MAXLIST];
+
+/*****************************************************************************
+ *
+ * ntpq_stripquotes
+ *
+ * Parses a given character buffer srcbuf and removes all quoted
+ * characters. The resulting string is copied to the specified
+ * resultbuf character buffer. E.g. \" will be translated into "
+ *
+ ****************************************************************************
+ * Parameters:
+ * resultbuf char* The resulting string without quoted
+ * characters
+ * srcbuf char* The buffer holding the original string
+ * datalen int The number of bytes stored in srcbuf
+ * maxlen int Max. number of bytes for resultbuf
+ *
+ * Returns:
+ * int number of chars that have been copied to
+ * resultbuf
+ ****************************************************************************/
+
+int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen )
+{
+ char* dst = resultbuf;
+ char* dep = resultbuf + maxlen - 1;
+ char* src = srcbuf;
+ char* sep = srcbuf + (datalen >= 0 ? datalen : 0);
+ int esc = 0;
+ int ch;
+
+ if (maxlen <= 0)
+ return 0;
+
+ while ((dst != dep) && (src != sep) && (ch = (u_char)*src++) != 0) {
+ if (esc) {
+ esc = 0;
+ switch (ch) {
+ /* skip and do not copy */
+ /* case '"':*/ /* quotes */
+ case 'n': /*newline*/
+ case 'r': /*carriage return*/
+ case 'g': /*bell*/
+ case 't': /*tab*/
+ continue;
+ default:
+ break;
+ }
+ } else {
+ switch (ch) {
+ case '\\':
+ esc = 1;
+ case '"':
+ continue;
+ default:
+ break;
+ }
+ }
+ *dst++ = (char)ch;
+ }
+ *dst = '\0';
+ return (int)(dst - resultbuf);
+}
+
+
+/*****************************************************************************
+ *
+ * ntpq_getvar
+ *
+ * This function parses a given buffer for a variable/value pair and
+ * copies the value of the requested variable into the specified
+ * varvalue buffer.
+ *
+ * It returns the number of bytes copied or zero for an empty result
+ * (=no matching variable found or empty value)
+ *
+ ****************************************************************************
+ * Parameters:
+ * resultbuf char* The resulting string without quoted
+ * characters
+ * datalen size_t The number of bytes stored in
+ * resultbuf
+ * varname char* Name of the required variable
+ * varvalue char* Where the value of the variable should
+ * be stored
+ * maxlen size_t Max. number of bytes for varvalue
+ *
+ * Returns:
+ * size_t number of chars that have been copied to
+ * varvalue
+ ****************************************************************************/
+
+size_t
+ntpq_getvar(
+ const char * resultbuf,
+ size_t datalen,
+ const char * varname,
+ char * varvalue,
+ size_t maxlen)
+{
+ char * name;
+ char * value;
+ size_t idatalen;
+
+ value = NULL;
+ idatalen = (int)datalen;
+
+ while (nextvar(&idatalen, &resultbuf, &name, &value)) {
+ if (strcmp(varname, name) == 0) {
+ ntpq_stripquotes(varvalue, value, strlen(value), maxlen);
+
+ return strlen(varvalue);
+ }
+ }
+
+ return 0;
+}
+
+
+/*****************************************************************************
+ *
+ * ntpq_queryhost
+ *
+ * Sends a mode 6 query packet to the current open host (see
+ * ntpq_openhost) and stores the requested variable set in the specified
+ * character buffer.
+ * It returns the number of bytes read or zero for an empty result
+ * (=no answer or empty value)
+ *
+ ****************************************************************************
+ * Parameters:
+ * VARSET u_short Which variable set should be
+ * read (PEERVARS or CLOCKVARS)
+ * association int The association ID that should be read
+ * 0 represents the ntpd instance itself
+ * resultbuf char* The resulting string without quoted
+ * characters
+ * maxlen int Max. number of bytes for varvalue
+ *
+ * Returns:
+ * int number of bytes that have been copied to
+ * resultbuf
+ * - OR -
+ * 0 (zero) if no reply has been received or
+ * another failure occured
+ ****************************************************************************/
+
+int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen)
+{
+ const char *datap;
+ int res;
+ size_t dsize;
+ u_short rstatus;
+
+ if ( numhosts > 0 )
+ res = doquery(VARSET,association,0,0, (char *)0, &rstatus, &dsize, &datap);
+ else
+ return 0;
+
+ if ( ( res != 0) || ( dsize == 0 ) ) /* no data */
+ return 0;
+
+ if ( dsize > maxlen)
+ dsize = maxlen;
+
+
+ /* fill result resultbuf */
+ memcpy(resultbuf, datap, dsize);
+
+ return dsize;
+}
+
+
+
+/*****************************************************************************
+ *
+ * ntpq_openhost
+ *
+ * Sets up a connection to the ntpd instance of a specified host. Note:
+ * There is no real "connection" established because NTP solely works
+ * based on UDP.
+ *
+ ****************************************************************************
+ * Parameters:
+ * hostname char* Hostname/IP of the host running ntpd
+ * fam int Address Family (AF_INET, AF_INET6, or 0)
+ *
+ * Returns:
+ * int 1 if the host connection could be set up, i.e.
+ * name resolution was succesful and/or IP address
+ * has been validated
+ * - OR -
+ * 0 (zero) if a failure occured
+ ****************************************************************************/
+
+int
+ntpq_openhost(
+ char *hostname,
+ int fam
+ )
+{
+ if ( openhost(hostname, fam) )
+ {
+ numhosts = 1;
+ } else {
+ numhosts = 0;
+ }
+
+ return numhosts;
+
+}
+
+
+/*****************************************************************************
+ *
+ * ntpq_closehost
+ *
+ * Cleans up a connection by closing the used socket. Should be called
+ * when no further queries are required for the currently used host.
+ *
+ ****************************************************************************
+ * Parameters:
+ * - none -
+ *
+ * Returns:
+ * int 0 (zero) if no host has been opened before
+ * - OR -
+ * the resultcode from the closesocket function call
+ ****************************************************************************/
+
+int ntpq_closehost(void)
+{
+ if ( numhosts )
+ return closesocket(sockfd);
+
+ return 0;
+}
+
+
+/*****************************************************************************
+ *
+ * ntpq_read_associations
+ *
+ * This function queries the ntp host for its associations and returns the
+ * number of associations found.
+ *
+ * It takes an u_short array as its first parameter, this array holds the
+ * IDs of the associations,
+ * the function will not write more entries than specified with the
+ * max_entries parameter.
+ *
+ * However, if more than max_entries associations were found, the return
+ * value of this function will reflect the real number, even if not all
+ * associations have been stored in the array.
+ *
+ ****************************************************************************
+ * Parameters:
+ * resultbuf u_short*Array that should hold the list of
+ * association IDs
+ * maxentries int maximum number of association IDs that can
+ * be stored in resultbuf
+ *
+ * Returns:
+ * int number of association IDs stored in resultbuf
+ * - OR -
+ * 0 (zero) if a failure occured or no association has
+ * been returned.
+ ****************************************************************************/
+
+ int ntpq_read_associations ( u_short resultbuf[], int max_entries )
+{
+ int i = 0;
+
+ if (ntpq_dogetassoc()) {
+
+ if(numassoc < max_entries)
+ max_entries = numassoc;
+
+ for (i=0;i<max_entries;i++)
+ resultbuf[i] = assoc_cache[i].assid;
+
+ return numassoc;
+ }
+
+ return 0;
+}
+
+
+
+
+/*****************************************************************************
+ *
+ * ntpq_get_assocs
+ *
+ * This function reads the associations of a previously selected (with
+ * ntpq_openhost) NTP host into its own (global) array and returns the
+ * number of associations found.
+ *
+ * The obtained association IDs can be read by using the ntpq_get_assoc_id
+ * function.
+ *
+ ****************************************************************************
+ * Parameters:
+ * - none -
+ *
+ * Returns:
+ * int number of association IDs stored in resultbuf
+ * - OR -
+ * 0 (zero) if a failure occured or no association has
+ * been returned.
+ ****************************************************************************/
+
+ int ntpq_get_assocs ( void )
+{
+ return ntpq_read_associations( ntpq_associations, MAXASSOC );
+}
+
+
+/*****************************************************************************
+ *
+ * ntpq_get_assoc_number
+ *
+ * This function returns for a given Association ID the association number
+ * in the internal association array, which is filled by the ntpq_get_assocs
+ * function.
+ *
+ ****************************************************************************
+ * Parameters:
+ * associd int requested associaton ID
+ *
+ * Returns:
+ * int the number of the association array element that is
+ * representing the given association ID
+ * - OR -
+ * -1 if a failure occured or no matching association
+ * ID has been found
+ ****************************************************************************/
+
+int ntpq_get_assoc_number ( associd_t associd )
+{
+ int i;
+
+ for (i=0;i<numassoc;i++) {
+ if (assoc_cache[i].assid == associd)
+ return i;
+ }
+
+ return -1;
+
+}
+
+
+/*****************************************************************************
+ *
+ * ntpq_read_assoc_peervars
+ *
+ * This function reads the peervars variable-set of a specified association
+ * from a NTP host and writes it to the result buffer specified, honoring
+ * the maxsize limit.
+ *
+ * It returns the number of bytes written or 0 when the variable-set is
+ * empty or failed to read.
+ *
+ ****************************************************************************
+ * Parameters:
+ * associd int requested associaton ID
+ * resultbuf char* character buffer where the variable set
+ * should be stored
+ * maxsize int the maximum number of bytes that can be
+ * written to resultbuf
+ *
+ * Returns:
+ * int number of chars that have been copied to
+ * resultbuf
+ * - OR -
+ * 0 (zero) if an error occured
+ ****************************************************************************/
+
+int
+ntpq_read_assoc_peervars(
+ associd_t associd,
+ char * resultbuf,
+ int maxsize
+ )
+{
+ const char * datap;
+ int res;
+ size_t dsize;
+ u_short rstatus;
+
+ res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
+ &dsize, &datap);
+ if (res != 0)
+ return 0;
+ if (dsize <= 0) {
+ if (numhosts > 1)
+ fprintf(stderr, "server=%s ", currenthost);
+ fprintf(stderr,
+ "***No information returned for association %d\n",
+ associd);
+
+ return 0;
+ }
+ if (dsize > maxsize)
+ dsize = maxsize;
+ memcpy(resultbuf, datap, dsize);
+
+ return dsize;
+}
+
+
+
+
+/*****************************************************************************
+ *
+ * ntpq_read_sysvars
+ *
+ * This function reads the sysvars variable-set from a NTP host and writes it
+ * to the result buffer specified, honoring the maxsize limit.
+ *
+ * It returns the number of bytes written or 0 when the variable-set is empty
+ * or could not be read.
+ *
+ ****************************************************************************
+ * Parameters:
+ * resultbuf char* character buffer where the variable set
+ * should be stored
+ * maxsize int the maximum number of bytes that can be
+ * written to resultbuf
+ *
+ * Returns:
+ * int number of chars that have been copied to
+ * resultbuf
+ * - OR -
+ * 0 (zero) if an error occured
+ ****************************************************************************/
+size_t
+ntpq_read_sysvars(
+ char * resultbuf,
+ size_t maxsize
+ )
+{
+ const char * datap;
+ int res;
+ size_t dsize;
+ u_short rstatus;
+
+ res = doquery(CTL_OP_READVAR, 0, 0, 0, NULL, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ if (numhosts > 1)
+ fprintf(stderr, "server=%s ", currenthost);
+ fprintf(stderr, "***No sysvar information returned\n");
+
+ return 0;
+ } else {
+ dsize = min(dsize, maxsize);
+ memcpy(resultbuf, datap, dsize);
+ }
+
+ return dsize;
+}
+
+
+/*****************************************************************************
+ * ntpq_get_assoc_allvars
+ *
+ * With this function all association variables for the specified association
+ * ID can be requested from a NTP host. They are stored internally and can be
+ * read by using the ntpq_get_peervar or ntpq_get_clockvar functions.
+ *
+ * Basically this is only a combination of the ntpq_get_assoc_peervars and
+ * ntpq_get_assoc_clockvars functions.
+ *
+ * It returns 1 if both variable-sets (peervars and clockvars) were
+ * received successfully. If one variable-set or both of them weren't
+ * received,
+ *
+ ****************************************************************************
+ * Parameters:
+ * associd int requested associaton ID
+ *
+ * Returns:
+ * int nonzero if at least one variable set could be read
+ * - OR -
+ * 0 (zero) if an error occured and both variable sets
+ * could not be read
+ ****************************************************************************/
+ int ntpq_get_assoc_allvars( associd_t associd )
+{
+ return ntpq_get_assoc_peervars ( associd ) &
+ ntpq_get_assoc_clockvars( associd );
+}
+
+
+
+
+/*****************************************************************************
+ *
+ * ntpq_get_sysvars
+ *
+ * The system variables of a NTP host can be requested by using this function
+ * and afterwards using ntpq_get_sysvar to read the single variable values.
+ *
+ ****************************************************************************
+ * Parameters:
+ * - none -
+ *
+ * Returns:
+ * int nonzero if the variable set could be read
+ * - OR -
+ * 0 (zero) if an error occured and the sysvars
+ * could not be read
+ ****************************************************************************/
+int
+ntpq_get_sysvars(void)
+{
+ sysvarlen = ntpq_read_sysvars(sysvars, sizeof(sysvars));
+ if (sysvarlen <= 0)
+ return 0;
+ else
+ return 1;
+}
+
+
+/*****************************************************************************
+ *
+ * ntp_get_peervar
+ *
+ * This function uses the variable-set which was read by using
+ * ntp_get_peervars and searches for a variable specified with varname. If
+ * such a variable exists, it writes its value into
+ * varvalue (maxlen specifies the size of this target buffer).
+ *
+ ****************************************************************************
+ * Parameters:
+ * varname char* requested variable name
+ * varvalue char* the buffer where the value should go into
+ * maxlen int maximum number of bytes that can be copied to
+ * varvalue
+ *
+ * Returns:
+ * int number of bytes copied to varvalue
+ * - OR -
+ * 0 (zero) if an error occured or the variable could
+ * not be found
+ ****************************************************************************/
+int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen)
+{
+ return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) );
+}
+
+
+
+/*****************************************************************************
+ *
+ * ntpq_get_assoc_peervars
+ *
+ * This function requests the peer variables of the specified association
+ * from a NTP host. In order to access the variable values, the function
+ * ntpq_get_peervar must be used.
+ *
+ ****************************************************************************
+ * Parameters:
+ * associd int requested associaton ID
+ *
+ * Returns:
+ * int 1 (one) if the peervars have been read
+ * - OR -
+ * 0 (zero) if an error occured and the variable set
+ * could not be read
+ ****************************************************************************/
+int
+ntpq_get_assoc_peervars(
+ associd_t associd
+ )
+{
+ peervarlen = ntpq_read_assoc_peervars(associd, peervars,
+ sizeof(peervars));
+ if (peervarlen <= 0) {
+ peervar_assoc = 0;
+
+ return 0;
+ }
+ peervar_assoc = associd;
+
+ return 1;
+}
+
+
+/*****************************************************************************
+ *
+ * ntp_read_assoc_clockvars
+ *
+ * This function reads the clockvars variable-set of a specified association
+ * from a NTP host and writes it to the result buffer specified, honoring
+ * the maxsize limit.
+ *
+ * It returns the number of bytes written or 0 when the variable-set is
+ * empty or failed to read.
+ *
+ ****************************************************************************
+ * Parameters:
+ * associd int requested associaton ID
+ * resultbuf char* character buffer where the variable set
+ * should be stored
+ * maxsize int the maximum number of bytes that can be
+ * written to resultbuf
+ *
+ * Returns:
+ * int number of chars that have been copied to
+ * resultbuf
+ * - OR -
+ * 0 (zero) if an error occured
+ ****************************************************************************/
+
+int
+ntpq_read_assoc_clockvars(
+ associd_t associd,
+ char * resultbuf,
+ int maxsize
+ )
+{
+ const char *datap;
+ int res;
+ size_t dsize;
+ u_short rstatus;
+
+ res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd,
+ 0, &rstatus, &dsize, &datap);
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ if (numhosts > 1) /* no information returned from server */
+ return 0;
+ } else {
+ if (dsize > maxsize)
+ dsize = maxsize;
+ memcpy(resultbuf, datap, dsize);
+ }
+
+ return dsize;
+}
+
+
+
+/*****************************************************************************
+ *
+ * ntpq_get_assoc_clocktype
+ *
+ * This function returns a clocktype value for a given association number
+ * (not ID!):
+ *
+ * NTP_CLOCKTYPE_UNKNOWN Unknown clock type
+ * NTP_CLOCKTYPE_BROADCAST Broadcast server
+ * NTP_CLOCKTYPE_LOCAL Local clock
+ * NTP_CLOCKTYPE_UNICAST Unicast server
+ * NTP_CLOCKTYPE_MULTICAST Multicast server
+ *
+ ****************************************************************************/
+int
+ntpq_get_assoc_clocktype(
+ int assoc_index
+ )
+{
+ associd_t associd;
+ int i;
+ int rc;
+ sockaddr_u dum_store;
+ char dstadr[LENHOSTNAME];
+ char resultbuf[NTPQ_BUFLEN];
+
+ if (assoc_index < 0 || assoc_index >= numassoc)
+ return -1;
+
+ associd = assoc_cache[assoc_index].assid;
+ if (associd == peervar_assoc) {
+ rc = ntpq_get_peervar("dstadr", dstadr, sizeof(dstadr));
+ } else {
+ i = ntpq_read_assoc_peervars(associd, resultbuf,
+ sizeof(resultbuf));
+ if (i <= 0)
+ return -1;
+ rc = ntpq_getvar(resultbuf, i, "dstadr", dstadr,
+ sizeof(dstadr));
+ }
+
+ if (0 != rc && decodenetnum(dstadr, &dum_store))
+ return ntpq_decodeaddrtype(&dum_store);
+
+ return -1;
+}
+
+
+
+/*****************************************************************************
+ *
+ * ntpq_get_assoc_clockvars
+ *
+ * With this function the clock variables of the specified association are
+ * requested from a NTP host. This makes only sense for associations with
+ * the type 'l' (Local Clock) and you should check this with
+ * ntpq_get_assoc_clocktype for each association, before you use this function
+ * on it.
+ *
+ ****************************************************************************
+ * Parameters:
+ * associd int requested associaton ID
+ *
+ * Returns:
+ * int 1 (one) if the clockvars have been read
+ * - OR -
+ * 0 (zero) if an error occured and the variable set
+ * could not be read
+ ****************************************************************************/
+int ntpq_get_assoc_clockvars( associd_t associd )
+{
+ if (NTP_CLOCKTYPE_LOCAL != ntpq_get_assoc_clocktype(
+ ntpq_get_assoc_number(associd)))
+ return 0;
+ clockvarlen = ntpq_read_assoc_clockvars( associd, clockvars,
+ sizeof(clockvars) );
+ if ( clockvarlen <= 0 ) {
+ clockvar_assoc = 0;
+ return 0;
+ } else {
+ clockvar_assoc = associd;
+ return 1;
+ }
+}
+
+
diff --git a/bsd/freebsd/contrib/ntp/ntpq/libntpq.h b/bsd/freebsd/contrib/ntp/ntpq/libntpq.h
new file mode 100644
index 0000000..74caf27
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/ntpq/libntpq.h
@@ -0,0 +1,109 @@
+/*****************************************************************************
+ *
+ * libntpq.h
+ *
+ * This is the wrapper library for ntpq, the NTP query utility.
+ * This library reuses the sourcecode from ntpq and exports a number
+ * of useful functions in a library that can be linked against applications
+ * that need to query the status of a running ntpd. The whole
+ * communcation is based on mode 6 packets.
+ *
+ * This header file can be used in applications that want to link against
+ * libntpq.
+ *
+ ****************************************************************************/
+
+#include "ntp_net.h"
+
+/* general purpose buffer size */
+#define NTPQ_BUFLEN 2048
+
+/* max. number of associations */
+#ifndef MAXASSOC
+#define MAXASSOC 1024
+#endif
+
+/* general purpose max array size definition */
+#ifndef MAXLIST
+#define MAXLIST 64
+#endif
+
+#ifndef LENHOSTNAME
+#define LENHOSTNAME 256 /* host name is max. 256 characters long */
+#endif
+
+/* NTP Status codes */
+#define NTP_STATUS_INVALID 0
+#define NTP_STATUS_FALSETICKER 1
+#define NTP_STATUS_EXCESS 2
+#define NTP_STATUS_OUTLIER 3
+#define NTP_STATUS_CANDIDATE 4
+#define NTP_STATUS_SELECTED 5
+#define NTP_STATUS_SYSPEER 6
+#define NTP_STATUS_PPSPEER 7
+
+/* NTP association type identifier */
+#define NTP_CLOCKTYPE_UNKNOWN '-'
+#define NTP_CLOCKTYPE_BROADCAST 'b'
+#define NTP_CLOCKTYPE_LOCAL 'l'
+#define NTP_CLOCKTYPE_UNICAST 'u'
+#define NTP_CLOCKTYPE_MULTICAST 'm'
+
+/* Variable Sets */
+#define PEERVARS CTL_OP_READVAR
+#define CLOCKVARS CTL_OP_CLOCKVAR
+
+/* Variable list struct */
+struct ntpq_varlist {
+ char *name;
+ char *value;
+};
+
+/* global variables used for holding snapshots of data */
+#ifndef LIBNTPQ_C
+extern char peervars[];
+extern int peervarlen;
+extern int peervar_assoc;
+extern char clockvars[];
+extern int clockvarlen;
+extern int clockvar_assoc;
+extern char sysvars[];
+extern int sysvarlen;
+extern char *ntpq_resultbuffer[];
+extern struct ntpq_varlist ntpq_varlist[MAXLIST];
+#endif
+
+
+
+/*
+ * Prototypes of exported libary functions
+ */
+
+/* from libntpq.c */
+extern int ntpq_openhost(char *, int);
+extern int ntpq_closehost(void);
+extern int ntpq_queryhost(unsigned short VARSET, associd_t association,
+ char *resultbuf, int maxlen);
+extern size_t ntpq_getvar(const char *resultbuf, size_t datalen,
+ const char *varname, char *varvalue,
+ size_t maxlen);
+extern int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen );
+extern int ntpq_queryhost_peervars(associd_t association, char *resultbuf, int maxlen);
+extern int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen);
+extern size_t ntpq_read_sysvars(char *resultbuf, size_t maxsize);
+extern int ntpq_get_sysvars( void );
+extern int ntpq_read_associations ( unsigned short resultbuf[], int max_entries );
+extern int ntpq_get_assocs ( void );
+extern int ntpq_get_assoc_number ( associd_t associd );
+extern int ntpq_get_assoc_peervars( associd_t associd );
+extern int ntpq_get_assoc_clockvars( associd_t associd );
+extern int ntpq_get_assoc_allvars( associd_t associd );
+extern int ntpq_get_assoc_clocktype(int assoc_index);
+extern int ntpq_read_assoc_peervars( associd_t associd, char *resultbuf, int maxsize );
+extern int ntpq_read_assoc_clockvars( associd_t associd, char *resultbuf, int maxsize );
+
+/* in libntpq_subs.c */
+extern int ntpq_dogetassoc(void);
+extern char ntpq_decodeaddrtype(sockaddr_u *sock);
+extern int ntpq_doquerylist(struct ntpq_varlist *, int, associd_t, int,
+ u_short *, size_t *, const char **datap);
diff --git a/bsd/freebsd/contrib/ntp/ntpq/libntpq_subs.c b/bsd/freebsd/contrib/ntp/ntpq/libntpq_subs.c
new file mode 100644
index 0000000..b232065
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/ntpq/libntpq_subs.c
@@ -0,0 +1,52 @@
+/*****************************************************************************
+ *
+ * libntpq_subs.c
+ *
+ * This is the second part of the wrapper library for ntpq, the NTP query utility.
+ * This library reuses the sourcecode from ntpq and exports a number
+ * of useful functions in a library that can be linked against applications
+ * that need to query the status of a running ntpd. The whole
+ * communcation is based on mode 6 packets.
+ *
+ * This source file exports the (private) functions from ntpq-subs.c
+ *
+ ****************************************************************************/
+
+
+#include "ntpq-subs.c"
+#include "libntpq.h"
+
+
+int ntpq_dogetassoc(void)
+{
+
+ if (dogetassoc(NULL))
+ return numassoc;
+ else
+ return 0;
+}
+
+/* the following functions are required internally by a number of libntpq functions
+ * and since they are defined as static in ntpq-subs.c, they need to be exported here
+ */
+
+char ntpq_decodeaddrtype(sockaddr_u *sock)
+{
+ return decodeaddrtype(sock);
+}
+
+int
+ntpq_doquerylist(
+ struct ntpq_varlist *vlist,
+ int op,
+ associd_t associd,
+ int auth,
+ u_short *rstatus,
+ size_t *dsize,
+ const char **datap
+ )
+{
+ return doquerylist((struct varlist *)vlist, op, associd, auth,
+ rstatus, dsize, datap);
+}
+
diff --git a/bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.c b/bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.c
new file mode 100644
index 0000000..ca08c3f
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.c
@@ -0,0 +1,1220 @@
+/*
+ * EDIT THIS FILE WITH CAUTION (ntpq-opts.c)
+ *
+ * It has been AutoGen-ed June 23, 2020 at 02:20:48 AM by AutoGen 5.18.5
+ * From the definitions ntpq-opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 41:1:16 templates.
+ *
+ * AutoOpts is a copyrighted work. This source file is not encumbered
+ * by AutoOpts licensing, but is provided under the licensing terms chosen
+ * by the ntpq author or copyright holder. AutoOpts is
+ * licensed under the terms of the LGPL. The redistributable library
+ * (``libopts'') is licensed under the terms of either the LGPL or, at the
+ * users discretion, the BSD license. See the AutoOpts and/or libopts sources
+ * for details.
+ *
+ * The ntpq program is copyrighted and licensed
+ * under the following terms:
+ *
+ * Copyright (C) 1992-2020 The University of Delaware and Network Time Foundation, all rights reserved.
+ * This is free software. It is licensed for use, modification and
+ * redistribution under the terms of the NTP License, copies of which
+ * can be seen at:
+ * <http://ntp.org/license>
+ * <http://opensource.org/licenses/ntp-license.php>
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and that
+ * both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name The University of Delaware not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The University of Delaware and Network Time Foundation makes no
+ * representations about the suitability this software for any purpose. It
+ * is provided "as is" without express or implied warranty.
+ */
+
+#ifndef __doxygen__
+#define OPTION_CODE_COMPILE 1
+#include "ntpq-opts.h"
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern FILE * option_usage_fp;
+#define zCopyright (ntpq_opt_strs+0)
+#define zLicenseDescrip (ntpq_opt_strs+341)
+
+/*
+ * global included definitions
+ */
+#ifdef __windows
+ extern int atoi(const char*);
+#else
+# include <stdlib.h>
+#endif
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+/**
+ * static const strings for ntpq options
+ */
+static char const ntpq_opt_strs[1977] =
+/* 0 */ "ntpq 4.2.8p15\n"
+ "Copyright (C) 1992-2020 The University of Delaware and Network Time Foundation, all rights reserved.\n"
+ "This is free software. It is licensed for use, modification and\n"
+ "redistribution under the terms of the NTP License, copies of which\n"
+ "can be seen at:\n"
+ " <http://ntp.org/license>\n"
+ " <http://opensource.org/licenses/ntp-license.php>\n\0"
+/* 341 */ "Permission to use, copy, modify, and distribute this software and its\n"
+ "documentation for any purpose with or without fee is hereby granted,\n"
+ "provided that the above copyright notice appears in all copies and that\n"
+ "both the copyright notice and this permission notice appear in supporting\n"
+ "documentation, and that the name The University of Delaware not be used in\n"
+ "advertising or publicity pertaining to distribution of the software without\n"
+ "specific, written prior permission. The University of Delaware and Network\n"
+ "Time Foundation makes no representations about the suitability this\n"
+ "software for any purpose. It is provided \"as is\" without express or\n"
+ "implied warranty.\n\0"
+/* 1009 */ "Force IPv4 name resolution\0"
+/* 1036 */ "IPV4\0"
+/* 1041 */ "ipv4\0"
+/* 1046 */ "Force IPv6 name resolution\0"
+/* 1073 */ "IPV6\0"
+/* 1078 */ "ipv6\0"
+/* 1083 */ "run a command and exit\0"
+/* 1106 */ "COMMAND\0"
+/* 1114 */ "command\0"
+/* 1122 */ "Increase debug verbosity level\0"
+/* 1153 */ "DEBUG_LEVEL\0"
+/* 1165 */ "debug-level\0"
+/* 1177 */ "Set the debug verbosity level\0"
+/* 1207 */ "SET_DEBUG_LEVEL\0"
+/* 1223 */ "set-debug-level\0"
+/* 1239 */ "Force ntpq to operate in interactive mode\0"
+/* 1281 */ "INTERACTIVE\0"
+/* 1293 */ "interactive\0"
+/* 1305 */ "numeric host addresses\0"
+/* 1328 */ "NUMERIC\0"
+/* 1336 */ "numeric\0"
+/* 1344 */ "Always output status line with readvar\0"
+/* 1383 */ "OLD_RV\0"
+/* 1390 */ "old-rv\0"
+/* 1397 */ "Print a list of the peers\0"
+/* 1423 */ "PEERS\0"
+/* 1429 */ "peers\0"
+/* 1435 */ "Set default display type for S2+ refids\0"
+/* 1475 */ "REFID\0"
+/* 1481 */ "refid\0"
+/* 1487 */ "Display the full 'remote' value\0"
+/* 1519 */ "WIDE\0"
+/* 1524 */ "wide\0"
+/* 1529 */ "display extended usage information and exit\0"
+/* 1573 */ "help\0"
+/* 1578 */ "extended usage information passed thru pager\0"
+/* 1623 */ "more-help\0"
+/* 1633 */ "output version information and exit\0"
+/* 1669 */ "version\0"
+/* 1677 */ "save the option state to a config file\0"
+/* 1716 */ "save-opts\0"
+/* 1726 */ "load options from a config file\0"
+/* 1758 */ "LOAD_OPTS\0"
+/* 1768 */ "no-load-opts\0"
+/* 1781 */ "no\0"
+/* 1784 */ "NTPQ\0"
+/* 1789 */ "ntpq - standard NTP query program - Ver. 4.2.8p15\n"
+ "Usage: %s [ -<flag> [<val>] | --<name>[{=| }<val>] ]... [ host ...]\n\0"
+/* 1909 */ "$HOME\0"
+/* 1915 */ ".\0"
+/* 1917 */ ".ntprc\0"
+/* 1924 */ "http://bugs.ntp.org, bugs@ntp.org\0"
+/* 1958 */ "ntpq 4.2.8p15\0"
+/* 1972 */ "hash";
+
+/**
+ * ipv4 option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the ipv4 option */
+#define IPV4_DESC (ntpq_opt_strs+1009)
+/** Upper-cased name for the ipv4 option */
+#define IPV4_NAME (ntpq_opt_strs+1036)
+/** Name string for the ipv4 option */
+#define IPV4_name (ntpq_opt_strs+1041)
+/** Other options that appear in conjunction with the ipv4 option */
+static int const aIpv4CantList[] = {
+ INDEX_OPT_IPV6, NO_EQUIVALENT };
+/** Compiled in flag settings for the ipv4 option */
+#define IPV4_FLAGS (OPTST_DISABLED)
+
+/**
+ * ipv6 option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the ipv6 option */
+#define IPV6_DESC (ntpq_opt_strs+1046)
+/** Upper-cased name for the ipv6 option */
+#define IPV6_NAME (ntpq_opt_strs+1073)
+/** Name string for the ipv6 option */
+#define IPV6_name (ntpq_opt_strs+1078)
+/** Other options that appear in conjunction with the ipv6 option */
+static int const aIpv6CantList[] = {
+ INDEX_OPT_IPV4, NO_EQUIVALENT };
+/** Compiled in flag settings for the ipv6 option */
+#define IPV6_FLAGS (OPTST_DISABLED)
+
+/**
+ * command option description:
+ */
+/** Descriptive text for the command option */
+#define COMMAND_DESC (ntpq_opt_strs+1083)
+/** Upper-cased name for the command option */
+#define COMMAND_NAME (ntpq_opt_strs+1106)
+/** Name string for the command option */
+#define COMMAND_name (ntpq_opt_strs+1114)
+/** Compiled in flag settings for the command option */
+#define COMMAND_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * debug-level option description:
+ */
+/** Descriptive text for the debug-level option */
+#define DEBUG_LEVEL_DESC (ntpq_opt_strs+1122)
+/** Upper-cased name for the debug-level option */
+#define DEBUG_LEVEL_NAME (ntpq_opt_strs+1153)
+/** Name string for the debug-level option */
+#define DEBUG_LEVEL_name (ntpq_opt_strs+1165)
+/** Compiled in flag settings for the debug-level option */
+#define DEBUG_LEVEL_FLAGS (OPTST_DISABLED)
+
+/**
+ * set-debug-level option description:
+ */
+/** Descriptive text for the set-debug-level option */
+#define SET_DEBUG_LEVEL_DESC (ntpq_opt_strs+1177)
+/** Upper-cased name for the set-debug-level option */
+#define SET_DEBUG_LEVEL_NAME (ntpq_opt_strs+1207)
+/** Name string for the set-debug-level option */
+#define SET_DEBUG_LEVEL_name (ntpq_opt_strs+1223)
+/** Compiled in flag settings for the set-debug-level option */
+#define SET_DEBUG_LEVEL_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+/**
+ * interactive option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the interactive option */
+#define INTERACTIVE_DESC (ntpq_opt_strs+1239)
+/** Upper-cased name for the interactive option */
+#define INTERACTIVE_NAME (ntpq_opt_strs+1281)
+/** Name string for the interactive option */
+#define INTERACTIVE_name (ntpq_opt_strs+1293)
+/** Other options that appear in conjunction with the interactive option */
+static int const aInteractiveCantList[] = {
+ INDEX_OPT_COMMAND,
+ INDEX_OPT_PEERS, NO_EQUIVALENT };
+/** Compiled in flag settings for the interactive option */
+#define INTERACTIVE_FLAGS (OPTST_DISABLED)
+
+/**
+ * numeric option description:
+ */
+/** Descriptive text for the numeric option */
+#define NUMERIC_DESC (ntpq_opt_strs+1305)
+/** Upper-cased name for the numeric option */
+#define NUMERIC_NAME (ntpq_opt_strs+1328)
+/** Name string for the numeric option */
+#define NUMERIC_name (ntpq_opt_strs+1336)
+/** Compiled in flag settings for the numeric option */
+#define NUMERIC_FLAGS (OPTST_DISABLED)
+
+/**
+ * old-rv option description:
+ */
+/** Descriptive text for the old-rv option */
+#define OLD_RV_DESC (ntpq_opt_strs+1344)
+/** Upper-cased name for the old-rv option */
+#define OLD_RV_NAME (ntpq_opt_strs+1383)
+/** Name string for the old-rv option */
+#define OLD_RV_name (ntpq_opt_strs+1390)
+/** Compiled in flag settings for the old-rv option */
+#define OLD_RV_FLAGS (OPTST_DISABLED)
+
+/**
+ * peers option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the peers option */
+#define PEERS_DESC (ntpq_opt_strs+1397)
+/** Upper-cased name for the peers option */
+#define PEERS_NAME (ntpq_opt_strs+1423)
+/** Name string for the peers option */
+#define PEERS_name (ntpq_opt_strs+1429)
+/** Other options that appear in conjunction with the peers option */
+static int const aPeersCantList[] = {
+ INDEX_OPT_INTERACTIVE, NO_EQUIVALENT };
+/** Compiled in flag settings for the peers option */
+#define PEERS_FLAGS (OPTST_DISABLED)
+
+/**
+ * refid option description:
+ */
+/** Descriptive text for the refid option */
+#define REFID_DESC (ntpq_opt_strs+1435)
+/** Upper-cased name for the refid option */
+#define REFID_NAME (ntpq_opt_strs+1475)
+/** Name string for the refid option */
+#define REFID_name (ntpq_opt_strs+1481)
+/** The compiled in default value for the refid option argument */
+#define REFID_DFT_ARG ((char const*)REFID_IPV4)
+/** Compiled in flag settings for the refid option */
+#define REFID_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_ENUMERATION))
+
+/**
+ * wide option description:
+ */
+/** Descriptive text for the wide option */
+#define WIDE_DESC (ntpq_opt_strs+1487)
+/** Upper-cased name for the wide option */
+#define WIDE_NAME (ntpq_opt_strs+1519)
+/** Name string for the wide option */
+#define WIDE_name (ntpq_opt_strs+1524)
+/** Compiled in flag settings for the wide option */
+#define WIDE_FLAGS (OPTST_DISABLED)
+
+/*
+ * Help/More_Help/Version option descriptions:
+ */
+#define HELP_DESC (ntpq_opt_strs+1529)
+#define HELP_name (ntpq_opt_strs+1573)
+#ifdef HAVE_WORKING_FORK
+#define MORE_HELP_DESC (ntpq_opt_strs+1578)
+#define MORE_HELP_name (ntpq_opt_strs+1623)
+#define MORE_HELP_FLAGS (OPTST_IMM | OPTST_NO_INIT)
+#else
+#define MORE_HELP_DESC HELP_DESC
+#define MORE_HELP_name HELP_name
+#define MORE_HELP_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#endif
+#ifdef NO_OPTIONAL_OPT_ARGS
+# define VER_FLAGS (OPTST_IMM | OPTST_NO_INIT)
+#else
+# define VER_FLAGS (OPTST_SET_ARGTYPE(OPARG_TYPE_STRING) | \
+ OPTST_ARG_OPTIONAL | OPTST_IMM | OPTST_NO_INIT)
+#endif
+#define VER_DESC (ntpq_opt_strs+1633)
+#define VER_name (ntpq_opt_strs+1669)
+#define SAVE_OPTS_DESC (ntpq_opt_strs+1677)
+#define SAVE_OPTS_name (ntpq_opt_strs+1716)
+#define LOAD_OPTS_DESC (ntpq_opt_strs+1726)
+#define LOAD_OPTS_NAME (ntpq_opt_strs+1758)
+#define NO_LOAD_OPTS_name (ntpq_opt_strs+1768)
+#define LOAD_OPTS_pfx (ntpq_opt_strs+1781)
+#define LOAD_OPTS_name (NO_LOAD_OPTS_name + 3)
+/**
+ * Declare option callback procedures
+ */
+extern tOptProc
+ ntpOptionPrintVersion, ntpq_custom_opt_handler, optionBooleanVal,
+ optionNestedVal, optionNumericVal, optionPagedUsage,
+ optionResetOpt, optionStackArg, optionTimeDate,
+ optionTimeVal, optionUnstackArg, optionVendorOption;
+static tOptProc
+ doOptDebug_Level, doOptRefid, doUsageOpt;
+#define VER_PROC ntpOptionPrintVersion
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Define the ntpq Option Descriptions.
+ * This is an array of OPTION_CT entries, one for each
+ * option that the ntpq program responds to.
+ */
+static tOptDesc optDesc[OPTION_CT] = {
+ { /* entry idx, value */ 0, VALUE_OPT_IPV4,
+ /* equiv idx, value */ 0, VALUE_OPT_IPV4,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ IPV4_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --ipv4 */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aIpv4CantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ IPV4_DESC, IPV4_NAME, IPV4_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 1, VALUE_OPT_IPV6,
+ /* equiv idx, value */ 1, VALUE_OPT_IPV6,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ IPV6_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --ipv6 */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aIpv6CantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ IPV6_DESC, IPV6_NAME, IPV6_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 2, VALUE_OPT_COMMAND,
+ /* equiv idx, value */ 2, VALUE_OPT_COMMAND,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ COMMAND_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --command */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ ntpq_custom_opt_handler,
+ /* desc, NAME, name */ COMMAND_DESC, COMMAND_NAME, COMMAND_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 3, VALUE_OPT_DEBUG_LEVEL,
+ /* equiv idx, value */ 3, VALUE_OPT_DEBUG_LEVEL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ DEBUG_LEVEL_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --debug-level */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptDebug_Level,
+ /* desc, NAME, name */ DEBUG_LEVEL_DESC, DEBUG_LEVEL_NAME, DEBUG_LEVEL_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 4, VALUE_OPT_SET_DEBUG_LEVEL,
+ /* equiv idx, value */ 4, VALUE_OPT_SET_DEBUG_LEVEL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ SET_DEBUG_LEVEL_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --set-debug-level */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionNumericVal,
+ /* desc, NAME, name */ SET_DEBUG_LEVEL_DESC, SET_DEBUG_LEVEL_NAME, SET_DEBUG_LEVEL_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 5, VALUE_OPT_INTERACTIVE,
+ /* equiv idx, value */ 5, VALUE_OPT_INTERACTIVE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ INTERACTIVE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --interactive */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aInteractiveCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ INTERACTIVE_DESC, INTERACTIVE_NAME, INTERACTIVE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 6, VALUE_OPT_NUMERIC,
+ /* equiv idx, value */ 6, VALUE_OPT_NUMERIC,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NUMERIC_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --numeric */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ NUMERIC_DESC, NUMERIC_NAME, NUMERIC_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 7, VALUE_OPT_OLD_RV,
+ /* equiv idx, value */ 7, VALUE_OPT_OLD_RV,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OLD_RV_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --old-rv */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ OLD_RV_DESC, OLD_RV_NAME, OLD_RV_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 8, VALUE_OPT_PEERS,
+ /* equiv idx, value */ 8, VALUE_OPT_PEERS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PEERS_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --peers */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aPeersCantList,
+ /* option proc */ ntpq_custom_opt_handler,
+ /* desc, NAME, name */ PEERS_DESC, PEERS_NAME, PEERS_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 9, VALUE_OPT_REFID,
+ /* equiv idx, value */ 9, VALUE_OPT_REFID,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ REFID_FLAGS, 0,
+ /* last opt argumnt */ { REFID_DFT_ARG },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptRefid,
+ /* desc, NAME, name */ REFID_DESC, REFID_NAME, REFID_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 10, VALUE_OPT_WIDE,
+ /* equiv idx, value */ 10, VALUE_OPT_WIDE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ WIDE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --wide */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ WIDE_DESC, WIDE_NAME, WIDE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_VERSION, VALUE_OPT_VERSION,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_VERSION,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ VER_FLAGS, AOUSE_VERSION,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ VER_PROC,
+ /* desc, NAME, name */ VER_DESC, NULL, VER_name,
+ /* disablement strs */ NULL, NULL },
+
+
+
+ { /* entry idx, value */ INDEX_OPT_HELP, VALUE_OPT_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_HELP,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_IMM | OPTST_NO_INIT, AOUSE_HELP,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doUsageOpt,
+ /* desc, NAME, name */ HELP_DESC, NULL, HELP_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_MORE_HELP, VALUE_OPT_MORE_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_MORE_HELP,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ MORE_HELP_FLAGS, AOUSE_MORE_HELP,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionPagedUsage,
+ /* desc, NAME, name */ MORE_HELP_DESC, NULL, MORE_HELP_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_SAVE_OPTS, VALUE_OPT_SAVE_OPTS,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_SAVE_OPTS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_SET_ARGTYPE(OPARG_TYPE_STRING)
+ | OPTST_ARG_OPTIONAL | OPTST_NO_INIT, AOUSE_SAVE_OPTS,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ SAVE_OPTS_DESC, NULL, SAVE_OPTS_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_LOAD_OPTS, VALUE_OPT_LOAD_OPTS,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_LOAD_OPTS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ OPTST_SET_ARGTYPE(OPARG_TYPE_STRING)
+ | OPTST_DISABLE_IMM, AOUSE_LOAD_OPTS,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionLoadOpt,
+ /* desc, NAME, name */ LOAD_OPTS_DESC, LOAD_OPTS_NAME, LOAD_OPTS_name,
+ /* disablement strs */ NO_LOAD_OPTS_name, LOAD_OPTS_pfx }
+};
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/** Reference to the upper cased version of ntpq. */
+#define zPROGNAME (ntpq_opt_strs+1784)
+/** Reference to the title line for ntpq usage. */
+#define zUsageTitle (ntpq_opt_strs+1789)
+/** ntpq configuration file name. */
+#define zRcName (ntpq_opt_strs+1917)
+/** Directories to search for ntpq config files. */
+static char const * const apzHomeList[3] = {
+ ntpq_opt_strs+1909,
+ ntpq_opt_strs+1915,
+ NULL };
+/** The ntpq program bug email address. */
+#define zBugsAddr (ntpq_opt_strs+1924)
+/** Clarification/explanation of what ntpq does. */
+#define zExplain (NULL)
+/** Extra detail explaining what ntpq does. */
+#define zDetail (NULL)
+/** The full version string for ntpq. */
+#define zFullVersion (ntpq_opt_strs+1958)
+/* extracted from optcode.tlib near line 364 */
+
+#if defined(ENABLE_NLS)
+# define OPTPROC_BASE OPTPROC_TRANSLATE
+ static tOptionXlateProc translate_option_strings;
+#else
+# define OPTPROC_BASE OPTPROC_NONE
+# define translate_option_strings NULL
+#endif /* ENABLE_NLS */
+
+#define ntpq_full_usage (NULL)
+#define ntpq_short_usage (NULL)
+
+#endif /* not defined __doxygen__ */
+
+/*
+ * Create the static procedure(s) declared above.
+ */
+/**
+ * The callout function that invokes the optionUsage function.
+ *
+ * @param[in] opts the AutoOpts option description structure
+ * @param[in] od the descriptor for the "help" (usage) option.
+ * @noreturn
+ */
+static void
+doUsageOpt(tOptions * opts, tOptDesc * od)
+{
+ int ex_code;
+ ex_code = NTPQ_EXIT_SUCCESS;
+ optionUsage(&ntpqOptions, ex_code);
+ /* NOTREACHED */
+ exit(1);
+ (void)opts;
+ (void)od;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the debug-level option.
+ *
+ * @param[in] pOptions the ntpq options data structure
+ * @param[in,out] pOptDesc the option descriptor for this option.
+ */
+static void
+doOptDebug_Level(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ /*
+ * Be sure the flag-code[0] handles special values for the options pointer
+ * viz. (poptions <= OPTPROC_EMIT_LIMIT) *and also* the special flag bit
+ * ((poptdesc->fOptState & OPTST_RESET) != 0) telling the option to
+ * reset its state.
+ */
+ /* extracted from debug-opt.def, line 15 */
+OPT_VALUE_SET_DEBUG_LEVEL++;
+ (void)pOptDesc;
+ (void)pOptions;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the refid option.
+ * Set the default display format for S2+ refids.
+ * @param[in] pOptions the ntpq options data structure
+ * @param[in,out] pOptDesc the option descriptor for this option.
+ */
+static void
+doOptRefid(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+
+/* extracted from optmain.tlib near line 945 */
+ static char const * const names[2] = {
+ ntpq_opt_strs+1972, ntpq_opt_strs+1041 };
+
+ if (pOptions <= OPTPROC_EMIT_LIMIT) {
+ (void) optionEnumerationVal(pOptions, pOptDesc, names, 2);
+ return; /* protect AutoOpts client code from internal callbacks */
+ }
+
+ pOptDesc->optArg.argEnum =
+ optionEnumerationVal(pOptions, pOptDesc, names, 2);
+}
+/* extracted from optmain.tlib near line 1250 */
+
+/**
+ * The directory containing the data associated with ntpq.
+ */
+#ifndef PKGDATADIR
+# define PKGDATADIR ""
+#endif
+
+/**
+ * Information about the person or institution that packaged ntpq
+ * for the current distribution.
+ */
+#ifndef WITH_PACKAGER
+# define ntpq_packager_info NULL
+#else
+/** Packager information for ntpq. */
+static char const ntpq_packager_info[] =
+ "Packaged by " WITH_PACKAGER
+
+# ifdef WITH_PACKAGER_VERSION
+ " ("WITH_PACKAGER_VERSION")"
+# endif
+
+# ifdef WITH_PACKAGER_BUG_REPORTS
+ "\nReport ntpq bugs to " WITH_PACKAGER_BUG_REPORTS
+# endif
+ "\n";
+#endif
+#ifndef __doxygen__
+
+#endif /* __doxygen__ */
+/**
+ * The option definitions for ntpq. The one structure that
+ * binds them all.
+ */
+tOptions ntpqOptions = {
+ OPTIONS_STRUCT_VERSION,
+ 0, NULL, /* original argc + argv */
+ ( OPTPROC_BASE
+ + OPTPROC_ERRSTOP
+ + OPTPROC_SHORTOPT
+ + OPTPROC_LONGOPT
+ + OPTPROC_NO_REQ_OPT
+ + OPTPROC_ENVIRON
+ + OPTPROC_MISUSE ),
+ 0, NULL, /* current option index, current option */
+ NULL, NULL, zPROGNAME,
+ zRcName, zCopyright, zLicenseDescrip,
+ zFullVersion, apzHomeList, zUsageTitle,
+ zExplain, zDetail, optDesc,
+ zBugsAddr, /* address to send bugs to */
+ NULL, NULL, /* extensions/saved state */
+ optionUsage, /* usage procedure */
+ translate_option_strings, /* translation procedure */
+ /*
+ * Indexes to special options
+ */
+ { INDEX_OPT_MORE_HELP, /* more-help option index */
+ INDEX_OPT_SAVE_OPTS, /* save option index */
+ NO_EQUIVALENT, /* '-#' option index */
+ NO_EQUIVALENT /* index of default opt */
+ },
+ 16 /* full option count */, 11 /* user option count */,
+ ntpq_full_usage, ntpq_short_usage,
+ NULL, NULL,
+ PKGDATADIR, ntpq_packager_info
+};
+
+#if ENABLE_NLS
+/**
+ * This code is designed to translate translatable option text for the
+ * ntpq program. These translations happen upon entry
+ * to optionProcess().
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_DCGETTEXT
+# include <gettext.h>
+#endif
+#include <autoopts/usage-txt.h>
+
+static char * AO_gettext(char const * pz);
+static void coerce_it(void ** s);
+
+/**
+ * AutoGen specific wrapper function for gettext. It relies on the macro _()
+ * to convert from English to the target language, then strdup-duplicates the
+ * result string. It tries the "libopts" domain first, then whatever has been
+ * set via the \a textdomain(3) call.
+ *
+ * @param[in] pz the input text used as a lookup key.
+ * @returns the translated text (if there is one),
+ * or the original text (if not).
+ */
+static char *
+AO_gettext(char const * pz)
+{
+ char * res;
+ if (pz == NULL)
+ return NULL;
+#ifdef HAVE_DCGETTEXT
+ /*
+ * While processing the option_xlateable_txt data, try to use the
+ * "libopts" domain. Once we switch to the option descriptor data,
+ * do *not* use that domain.
+ */
+ if (option_xlateable_txt.field_ct != 0) {
+ res = dgettext("libopts", pz);
+ if (res == pz)
+ res = (char *)VOIDP(_(pz));
+ } else
+ res = (char *)VOIDP(_(pz));
+#else
+ res = (char *)VOIDP(_(pz));
+#endif
+ if (res == pz)
+ return res;
+ res = strdup(res);
+ if (res == NULL) {
+ fputs(_("No memory for duping translated strings\n"), stderr);
+ exit(NTPQ_EXIT_FAILURE);
+ }
+ return res;
+}
+
+/**
+ * All the pointers we use are marked "* const", but they are stored in
+ * writable memory. Coerce the mutability and set the pointer.
+ */
+static void coerce_it(void ** s) { *s = AO_gettext(*s);
+}
+
+/**
+ * Translate all the translatable strings in the ntpqOptions
+ * structure defined above. This is done only once.
+ */
+static void
+translate_option_strings(void)
+{
+ tOptions * const opts = &ntpqOptions;
+
+ /*
+ * Guard against re-translation. It won't work. The strings will have
+ * been changed by the first pass through this code. One shot only.
+ */
+ if (option_xlateable_txt.field_ct != 0) {
+ /*
+ * Do the translations. The first pointer follows the field count
+ * field. The field count field is the size of a pointer.
+ */
+ char ** ppz = (char**)VOIDP(&(option_xlateable_txt));
+ int ix = option_xlateable_txt.field_ct;
+
+ do {
+ ppz++; /* skip over field_ct */
+ *ppz = AO_gettext(*ppz);
+ } while (--ix > 0);
+ /* prevent re-translation and disable "libopts" domain lookup */
+ option_xlateable_txt.field_ct = 0;
+
+ coerce_it(VOIDP(&(opts->pzCopyright)));
+ coerce_it(VOIDP(&(opts->pzCopyNotice)));
+ coerce_it(VOIDP(&(opts->pzFullVersion)));
+ coerce_it(VOIDP(&(opts->pzUsageTitle)));
+ coerce_it(VOIDP(&(opts->pzExplain)));
+ coerce_it(VOIDP(&(opts->pzDetail)));
+ {
+ tOptDesc * od = opts->pOptDesc;
+ for (ix = opts->optCt; ix > 0; ix--, od++)
+ coerce_it(VOIDP(&(od->pzText)));
+ }
+ }
+}
+#endif /* ENABLE_NLS */
+
+#ifdef DO_NOT_COMPILE_THIS_CODE_IT_IS_FOR_GETTEXT
+/** I18N function strictly for xgettext. Do not compile. */
+static void bogus_function(void) {
+ /* TRANSLATORS:
+
+ The following dummy function was crated solely so that xgettext can
+ extract the correct strings. These strings are actually referenced
+ by a field name in the ntpqOptions structure noted in the
+ comments below. The literal text is defined in ntpq_opt_strs.
+
+ NOTE: the strings below are segmented with respect to the source string
+ ntpq_opt_strs. The strings above are handed off for translation
+ at run time a paragraph at a time. Consequently, they are presented here
+ for translation a paragraph at a time.
+
+ ALSO: often the description for an option will reference another option
+ by name. These are set off with apostrophe quotes (I hope). Do not
+ translate option names.
+ */
+ /* referenced via ntpqOptions.pzCopyright */
+ puts(_("ntpq 4.2.8p15\n\
+Copyright (C) 1992-2020 The University of Delaware and Network Time Foundation, all rights reserved.\n\
+This is free software. It is licensed for use, modification and\n\
+redistribution under the terms of the NTP License, copies of which\n\
+can be seen at:\n"));
+ puts(_(" <http://ntp.org/license>\n\
+ <http://opensource.org/licenses/ntp-license.php>\n"));
+
+ /* referenced via ntpqOptions.pzCopyNotice */
+ puts(_("Permission to use, copy, modify, and distribute this software and its\n\
+documentation for any purpose with or without fee is hereby granted,\n\
+provided that the above copyright notice appears in all copies and that\n\
+both the copyright notice and this permission notice appear in supporting\n\
+documentation, and that the name The University of Delaware not be used in\n\
+advertising or publicity pertaining to distribution of the software without\n\
+specific, written prior permission. The University of Delaware and Network\n\
+Time Foundation makes no representations about the suitability this\n\
+software for any purpose. It is provided \"as is\" without express or\n\
+implied warranty.\n"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("Force IPv4 name resolution"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("Force IPv6 name resolution"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("run a command and exit"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("Increase debug verbosity level"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("Set the debug verbosity level"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("Force ntpq to operate in interactive mode"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("numeric host addresses"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("Always output status line with readvar"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("Print a list of the peers"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("Set default display type for S2+ refids"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("Display the full 'remote' value"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("display extended usage information and exit"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("extended usage information passed thru pager"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("output version information and exit"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("save the option state to a config file"));
+
+ /* referenced via ntpqOptions.pOptDesc->pzText */
+ puts(_("load options from a config file"));
+
+ /* referenced via ntpqOptions.pzUsageTitle */
+ puts(_("ntpq - standard NTP query program - Ver. 4.2.8p15\n\
+Usage: %s [ -<flag> [<val>] | --<name>[{=| }<val>] ]... [ host ...]\n"));
+
+ /* referenced via ntpqOptions.pzFullVersion */
+ puts(_("ntpq 4.2.8p15"));
+
+ /* referenced via ntpqOptions.pzFullUsage */
+ puts(_("<<<NOT-FOUND>>>"));
+
+ /* referenced via ntpqOptions.pzShortUsage */
+ puts(_("<<<NOT-FOUND>>>"));
+ /* LIBOPTS-MESSAGES: */
+#line 67 "../autoopts.c"
+ puts(_("allocation of %d bytes failed\n"));
+#line 93 "../autoopts.c"
+ puts(_("allocation of %d bytes failed\n"));
+#line 53 "../init.c"
+ puts(_("AutoOpts function called without option descriptor\n"));
+#line 86 "../init.c"
+ puts(_("\tThis exceeds the compiled library version: "));
+#line 84 "../init.c"
+ puts(_("Automated Options Processing Error!\n"
+ "\t%s called AutoOpts function with structure version %d:%d:%d.\n"));
+#line 80 "../autoopts.c"
+ puts(_("realloc of %d bytes at 0x%p failed\n"));
+#line 88 "../init.c"
+ puts(_("\tThis is less than the minimum library version: "));
+#line 121 "../version.c"
+ puts(_("Automated Options version %s\n"
+ "\tCopyright (C) 1999-2014 by Bruce Korb - all rights reserved\n"));
+#line 87 "../makeshell.c"
+ puts(_("(AutoOpts bug): %s.\n"));
+#line 90 "../reset.c"
+ puts(_("optionResetOpt() called, but reset-option not configured"));
+#line 292 "../usage.c"
+ puts(_("could not locate the 'help' option"));
+#line 336 "../autoopts.c"
+ puts(_("optionProcess() was called with invalid data"));
+#line 748 "../usage.c"
+ puts(_("invalid argument type specified"));
+#line 598 "../find.c"
+ puts(_("defaulted to option with optional arg"));
+#line 76 "../alias.c"
+ puts(_("aliasing option is out of range."));
+#line 234 "../enum.c"
+ puts(_("%s error: the keyword '%s' is ambiguous for %s\n"));
+#line 108 "../find.c"
+ puts(_(" The following options match:\n"));
+#line 293 "../find.c"
+ puts(_("%s: ambiguous option name: %s (matches %d options)\n"));
+#line 161 "../check.c"
+ puts(_("%s: Command line arguments required\n"));
+#line 43 "../alias.c"
+ puts(_("%d %s%s options allowed\n"));
+#line 94 "../makeshell.c"
+ puts(_("%s error %d (%s) calling %s for '%s'\n"));
+#line 306 "../makeshell.c"
+ puts(_("interprocess pipe"));
+#line 168 "../version.c"
+ puts(_("error: version option argument '%c' invalid. Use:\n"
+ "\t'v' - version only\n"
+ "\t'c' - version and copyright\n"
+ "\t'n' - version and full copyright notice\n"));
+#line 58 "../check.c"
+ puts(_("%s error: the '%s' and '%s' options conflict\n"));
+#line 217 "../find.c"
+ puts(_("%s: The '%s' option has been disabled."));
+#line 430 "../find.c"
+ puts(_("%s: The '%s' option has been disabled."));
+#line 38 "../alias.c"
+ puts(_("-equivalence"));
+#line 469 "../find.c"
+ puts(_("%s: illegal option -- %c\n"));
+#line 110 "../reset.c"
+ puts(_("%s: illegal option -- %c\n"));
+#line 271 "../find.c"
+ puts(_("%s: illegal option -- %s\n"));
+#line 755 "../find.c"
+ puts(_("%s: illegal option -- %s\n"));
+#line 118 "../reset.c"
+ puts(_("%s: illegal option -- %s\n"));
+#line 335 "../find.c"
+ puts(_("%s: unknown vendor extension option -- %s\n"));
+#line 159 "../enum.c"
+ puts(_(" or an integer from %d through %d\n"));
+#line 169 "../enum.c"
+ puts(_(" or an integer from %d through %d\n"));
+#line 747 "../usage.c"
+ puts(_("%s error: invalid option descriptor for %s\n"));
+#line 1081 "../usage.c"
+ puts(_("%s error: invalid option descriptor for %s\n"));
+#line 385 "../find.c"
+ puts(_("%s: invalid option name: %s\n"));
+#line 527 "../find.c"
+ puts(_("%s: The '%s' option requires an argument.\n"));
+#line 156 "../autoopts.c"
+ puts(_("(AutoOpts bug): Equivalenced option '%s' was equivalenced to both\n"
+ "\t'%s' and '%s'."));
+#line 94 "../check.c"
+ puts(_("%s error: The %s option is required\n"));
+#line 632 "../find.c"
+ puts(_("%s: The '%s' option cannot have an argument.\n"));
+#line 151 "../check.c"
+ puts(_("%s: Command line arguments are not allowed.\n"));
+#line 535 "../save.c"
+ puts(_("error %d (%s) creating %s\n"));
+#line 234 "../enum.c"
+ puts(_("%s error: '%s' does not match any %s keywords.\n"));
+#line 93 "../reset.c"
+ puts(_("%s error: The '%s' option requires an argument.\n"));
+#line 184 "../save.c"
+ puts(_("error %d (%s) stat-ing %s\n"));
+#line 238 "../save.c"
+ puts(_("error %d (%s) stat-ing %s\n"));
+#line 143 "../restore.c"
+ puts(_("%s error: no saved option state\n"));
+#line 231 "../autoopts.c"
+ puts(_("'%s' is not a command line option.\n"));
+#line 111 "../time.c"
+ puts(_("%s error: '%s' is not a recognizable date/time.\n"));
+#line 132 "../save.c"
+ puts(_("'%s' not defined\n"));
+#line 50 "../time.c"
+ puts(_("%s error: '%s' is not a recognizable time duration.\n"));
+#line 92 "../check.c"
+ puts(_("%s error: The %s option must appear %d times.\n"));
+#line 164 "../numeric.c"
+ puts(_("%s error: '%s' is not a recognizable number.\n"));
+#line 200 "../enum.c"
+ puts(_("%s error: %s exceeds %s keyword count\n"));
+#line 330 "../usage.c"
+ puts(_("Try '%s %s' for more information.\n"));
+#line 45 "../alias.c"
+ puts(_("one %s%s option allowed\n"));
+#line 208 "../makeshell.c"
+ puts(_("standard output"));
+#line 943 "../makeshell.c"
+ puts(_("standard output"));
+#line 274 "../usage.c"
+ puts(_("standard output"));
+#line 415 "../usage.c"
+ puts(_("standard output"));
+#line 625 "../usage.c"
+ puts(_("standard output"));
+#line 175 "../version.c"
+ puts(_("standard output"));
+#line 274 "../usage.c"
+ puts(_("standard error"));
+#line 415 "../usage.c"
+ puts(_("standard error"));
+#line 625 "../usage.c"
+ puts(_("standard error"));
+#line 175 "../version.c"
+ puts(_("standard error"));
+#line 208 "../makeshell.c"
+ puts(_("write"));
+#line 943 "../makeshell.c"
+ puts(_("write"));
+#line 273 "../usage.c"
+ puts(_("write"));
+#line 414 "../usage.c"
+ puts(_("write"));
+#line 624 "../usage.c"
+ puts(_("write"));
+#line 174 "../version.c"
+ puts(_("write"));
+#line 60 "../numeric.c"
+ puts(_("%s error: %s option value %ld is out of range.\n"));
+#line 44 "../check.c"
+ puts(_("%s error: %s option requires the %s option\n"));
+#line 131 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+#line 183 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+#line 237 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+#line 256 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+#line 534 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+ /* END-LIBOPTS-MESSAGES */
+
+ /* USAGE-TEXT: */
+#line 873 "../usage.c"
+ puts(_("\t\t\t\t- an alternate for '%s'\n"));
+#line 1148 "../usage.c"
+ puts(_("Version, usage and configuration options:"));
+#line 924 "../usage.c"
+ puts(_("\t\t\t\t- default option for unnamed options\n"));
+#line 837 "../usage.c"
+ puts(_("\t\t\t\t- disabled as '--%s'\n"));
+#line 1117 "../usage.c"
+ puts(_(" --- %-14s %s\n"));
+#line 1115 "../usage.c"
+ puts(_("This option has been disabled"));
+#line 864 "../usage.c"
+ puts(_("\t\t\t\t- enabled by default\n"));
+#line 40 "../alias.c"
+ puts(_("%s error: only "));
+#line 1194 "../usage.c"
+ puts(_(" - examining environment variables named %s_*\n"));
+#line 168 "../file.c"
+ puts(_("\t\t\t\t- file must not pre-exist\n"));
+#line 172 "../file.c"
+ puts(_("\t\t\t\t- file must pre-exist\n"));
+#line 380 "../usage.c"
+ puts(_("Options are specified by doubled hyphens and their name or by a single\n"
+ "hyphen and the flag character.\n"));
+#line 921 "../makeshell.c"
+ puts(_("\n"
+ "= = = = = = = =\n\n"
+ "This incarnation of genshell will produce\n"
+ "a shell script to parse the options for %s:\n\n"));
+#line 166 "../enum.c"
+ puts(_(" or an integer mask with any of the lower %d bits set\n"));
+#line 897 "../usage.c"
+ puts(_("\t\t\t\t- is a set membership option\n"));
+#line 918 "../usage.c"
+ puts(_("\t\t\t\t- must appear between %d and %d times\n"));
+#line 382 "../usage.c"
+ puts(_("Options are specified by single or double hyphens and their name.\n"));
+#line 904 "../usage.c"
+ puts(_("\t\t\t\t- may appear multiple times\n"));
+#line 891 "../usage.c"
+ puts(_("\t\t\t\t- may not be preset\n"));
+#line 1309 "../usage.c"
+ puts(_(" Arg Option-Name Description\n"));
+#line 1245 "../usage.c"
+ puts(_(" Flg Arg Option-Name Description\n"));
+#line 1303 "../usage.c"
+ puts(_(" Flg Arg Option-Name Description\n"));
+#line 1304 "../usage.c"
+ puts(_(" %3s %s"));
+#line 1310 "../usage.c"
+ puts(_(" %3s %s"));
+#line 387 "../usage.c"
+ puts(_("The '-#<number>' option may omit the hash char\n"));
+#line 383 "../usage.c"
+ puts(_("All arguments are named options.\n"));
+#line 971 "../usage.c"
+ puts(_(" - reading file %s"));
+#line 409 "../usage.c"
+ puts(_("\n"
+ "Please send bug reports to: <%s>\n"));
+#line 100 "../version.c"
+ puts(_("\n"
+ "Please send bug reports to: <%s>\n"));
+#line 129 "../version.c"
+ puts(_("\n"
+ "Please send bug reports to: <%s>\n"));
+#line 903 "../usage.c"
+ puts(_("\t\t\t\t- may NOT appear - preset only\n"));
+#line 944 "../usage.c"
+ puts(_("\n"
+ "The following option preset mechanisms are supported:\n"));
+#line 1192 "../usage.c"
+ puts(_("\n"
+ "The following option preset mechanisms are supported:\n"));
+#line 682 "../usage.c"
+ puts(_("prohibits these options:\n"));
+#line 677 "../usage.c"
+ puts(_("prohibits the option '%s'\n"));
+#line 81 "../numeric.c"
+ puts(_("%s%ld to %ld"));
+#line 79 "../numeric.c"
+ puts(_("%sgreater than or equal to %ld"));
+#line 75 "../numeric.c"
+ puts(_("%s%ld exactly"));
+#line 68 "../numeric.c"
+ puts(_("%sit must lie in one of the ranges:\n"));
+#line 68 "../numeric.c"
+ puts(_("%sit must be in the range:\n"));
+#line 88 "../numeric.c"
+ puts(_(", or\n"));
+#line 66 "../numeric.c"
+ puts(_("%sis scalable with a suffix: k/K/m/M/g/G/t/T\n"));
+#line 77 "../numeric.c"
+ puts(_("%sless than or equal to %ld"));
+#line 390 "../usage.c"
+ puts(_("Operands and options may be intermixed. They will be reordered.\n"));
+#line 652 "../usage.c"
+ puts(_("requires the option '%s'\n"));
+#line 655 "../usage.c"
+ puts(_("requires these options:\n"));
+#line 1321 "../usage.c"
+ puts(_(" Arg Option-Name Req? Description\n"));
+#line 1315 "../usage.c"
+ puts(_(" Flg Arg Option-Name Req? Description\n"));
+#line 167 "../enum.c"
+ puts(_("or you may use a numeric representation. Preceding these with a '!'\n"
+ "will clear the bits, specifying 'none' will clear all bits, and 'all'\n"
+ "will set them all. Multiple entries may be passed as an option\n"
+ "argument list.\n"));
+#line 910 "../usage.c"
+ puts(_("\t\t\t\t- may appear up to %d times\n"));
+#line 77 "../enum.c"
+ puts(_("The valid \"%s\" option keywords are:\n"));
+#line 1152 "../usage.c"
+ puts(_("The next option supports vendor supported extra options:"));
+#line 773 "../usage.c"
+ puts(_("These additional options are:"));
+ /* END-USAGE-TEXT */
+}
+#endif /* uncompilable code */
+#ifdef __cplusplus
+}
+#endif
+/* ntpq-opts.c ends here */
diff --git a/bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.h b/bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.h
new file mode 100644
index 0000000..9bc23b8
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.h
@@ -0,0 +1,307 @@
+/*
+ * EDIT THIS FILE WITH CAUTION (ntpq-opts.h)
+ *
+ * It has been AutoGen-ed June 23, 2020 at 02:20:48 AM by AutoGen 5.18.5
+ * From the definitions ntpq-opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 41:1:16 templates.
+ *
+ * AutoOpts is a copyrighted work. This header file is not encumbered
+ * by AutoOpts licensing, but is provided under the licensing terms chosen
+ * by the ntpq author or copyright holder. AutoOpts is
+ * licensed under the terms of the LGPL. The redistributable library
+ * (``libopts'') is licensed under the terms of either the LGPL or, at the
+ * users discretion, the BSD license. See the AutoOpts and/or libopts sources
+ * for details.
+ *
+ * The ntpq program is copyrighted and licensed
+ * under the following terms:
+ *
+ * Copyright (C) 1992-2020 The University of Delaware and Network Time Foundation, all rights reserved.
+ * This is free software. It is licensed for use, modification and
+ * redistribution under the terms of the NTP License, copies of which
+ * can be seen at:
+ * <http://ntp.org/license>
+ * <http://opensource.org/licenses/ntp-license.php>
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and that
+ * both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name The University of Delaware not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The University of Delaware and Network Time Foundation makes no
+ * representations about the suitability this software for any purpose. It
+ * is provided "as is" without express or implied warranty.
+ */
+/**
+ * This file contains the programmatic interface to the Automated
+ * Options generated for the ntpq program.
+ * These macros are documented in the AutoGen info file in the
+ * "AutoOpts" chapter. Please refer to that doc for usage help.
+ */
+#ifndef AUTOOPTS_NTPQ_OPTS_H_GUARD
+#define AUTOOPTS_NTPQ_OPTS_H_GUARD 1
+#include "config.h"
+#include <autoopts/options.h>
+
+/**
+ * Ensure that the library used for compiling this generated header is at
+ * least as new as the version current when the header template was released
+ * (not counting patch version increments). Also ensure that the oldest
+ * tolerable version is at least as old as what was current when the header
+ * template was released.
+ */
+#define AO_TEMPLATE_VERSION 167937
+#if (AO_TEMPLATE_VERSION < OPTIONS_MINIMUM_VERSION) \
+ || (AO_TEMPLATE_VERSION > OPTIONS_STRUCT_VERSION)
+# error option template version mismatches autoopts/options.h header
+ Choke Me.
+#endif
+
+/**
+ * Enumeration of each option type for ntpq
+ */
+typedef enum {
+ INDEX_OPT_IPV4 = 0,
+ INDEX_OPT_IPV6 = 1,
+ INDEX_OPT_COMMAND = 2,
+ INDEX_OPT_DEBUG_LEVEL = 3,
+ INDEX_OPT_SET_DEBUG_LEVEL = 4,
+ INDEX_OPT_INTERACTIVE = 5,
+ INDEX_OPT_NUMERIC = 6,
+ INDEX_OPT_OLD_RV = 7,
+ INDEX_OPT_PEERS = 8,
+ INDEX_OPT_REFID = 9,
+ INDEX_OPT_WIDE = 10,
+ INDEX_OPT_VERSION = 11,
+ INDEX_OPT_HELP = 12,
+ INDEX_OPT_MORE_HELP = 13,
+ INDEX_OPT_SAVE_OPTS = 14,
+ INDEX_OPT_LOAD_OPTS = 15
+} teOptIndex;
+/** count of all options for ntpq */
+#define OPTION_CT 16
+/** ntpq version */
+#define NTPQ_VERSION "4.2.8p15"
+/** Full ntpq version text */
+#define NTPQ_FULL_VERSION "ntpq 4.2.8p15"
+
+/**
+ * Interface defines for all options. Replace "n" with the UPPER_CASED
+ * option name (as in the teOptIndex enumeration above).
+ * e.g. HAVE_OPT(IPV4)
+ */
+#define DESC(n) (ntpqOptions.pOptDesc[INDEX_OPT_## n])
+/** 'true' if an option has been specified in any way */
+#define HAVE_OPT(n) (! UNUSED_OPT(& DESC(n)))
+/** The string argument to an option. The argument type must be \"string\". */
+#define OPT_ARG(n) (DESC(n).optArg.argString)
+/** Mask the option state revealing how an option was specified.
+ * It will be one and only one of \a OPTST_SET, \a OPTST_PRESET,
+ * \a OPTST_DEFINED, \a OPTST_RESET or zero.
+ */
+#define STATE_OPT(n) (DESC(n).fOptState & OPTST_SET_MASK)
+/** Count of option's occurrances *on the command line*. */
+#define COUNT_OPT(n) (DESC(n).optOccCt)
+/** mask of \a OPTST_SET and \a OPTST_DEFINED. */
+#define ISSEL_OPT(n) (SELECTED_OPT(&DESC(n)))
+/** 'true' if \a HAVE_OPT would yield 'false'. */
+#define ISUNUSED_OPT(n) (UNUSED_OPT(& DESC(n)))
+/** 'true' if OPTST_DISABLED bit not set. */
+#define ENABLED_OPT(n) (! DISABLED_OPT(& DESC(n)))
+/** number of stacked option arguments.
+ * Valid only for stacked option arguments. */
+#define STACKCT_OPT(n) (((tArgList*)(DESC(n).optCookie))->useCt)
+/** stacked argument vector.
+ * Valid only for stacked option arguments. */
+#define STACKLST_OPT(n) (((tArgList*)(DESC(n).optCookie))->apzArgs)
+/** Reset an option. */
+#define CLEAR_OPT(n) STMTS( \
+ DESC(n).fOptState &= OPTST_PERSISTENT_MASK; \
+ if ((DESC(n).fOptState & OPTST_INITENABLED) == 0) \
+ DESC(n).fOptState |= OPTST_DISABLED; \
+ DESC(n).optCookie = NULL )
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Enumeration of ntpq exit codes
+ */
+typedef enum {
+ NTPQ_EXIT_SUCCESS = 0,
+ NTPQ_EXIT_FAILURE = 1,
+ NTPQ_EXIT_USAGE_ERROR = 64,
+ NTPQ_EXIT_NO_CONFIG_INPUT = 66,
+ NTPQ_EXIT_LIBOPTS_FAILURE = 70
+} ntpq_exit_code_t;
+/** @} */
+/**
+ * Make sure there are no #define name conflicts with the option names
+ */
+#ifndef NO_OPTION_NAME_WARNINGS
+# ifdef IPV4
+# warning undefining IPV4 due to option name conflict
+# undef IPV4
+# endif
+# ifdef IPV6
+# warning undefining IPV6 due to option name conflict
+# undef IPV6
+# endif
+# ifdef COMMAND
+# warning undefining COMMAND due to option name conflict
+# undef COMMAND
+# endif
+# ifdef DEBUG_LEVEL
+# warning undefining DEBUG_LEVEL due to option name conflict
+# undef DEBUG_LEVEL
+# endif
+# ifdef SET_DEBUG_LEVEL
+# warning undefining SET_DEBUG_LEVEL due to option name conflict
+# undef SET_DEBUG_LEVEL
+# endif
+# ifdef INTERACTIVE
+# warning undefining INTERACTIVE due to option name conflict
+# undef INTERACTIVE
+# endif
+# ifdef NUMERIC
+# warning undefining NUMERIC due to option name conflict
+# undef NUMERIC
+# endif
+# ifdef OLD_RV
+# warning undefining OLD_RV due to option name conflict
+# undef OLD_RV
+# endif
+# ifdef PEERS
+# warning undefining PEERS due to option name conflict
+# undef PEERS
+# endif
+# ifdef REFID
+# warning undefining REFID due to option name conflict
+# undef REFID
+# endif
+# ifdef WIDE
+# warning undefining WIDE due to option name conflict
+# undef WIDE
+# endif
+#else /* NO_OPTION_NAME_WARNINGS */
+# undef IPV4
+# undef IPV6
+# undef COMMAND
+# undef DEBUG_LEVEL
+# undef SET_DEBUG_LEVEL
+# undef INTERACTIVE
+# undef NUMERIC
+# undef OLD_RV
+# undef PEERS
+# undef REFID
+# undef WIDE
+#endif /* NO_OPTION_NAME_WARNINGS */
+
+/**
+ * Interface defines for specific options.
+ * @{
+ */
+#define VALUE_OPT_IPV4 '4'
+#define VALUE_OPT_IPV6 '6'
+#define VALUE_OPT_COMMAND 'c'
+#define VALUE_OPT_DEBUG_LEVEL 'd'
+#define VALUE_OPT_SET_DEBUG_LEVEL 'D'
+
+#define OPT_VALUE_SET_DEBUG_LEVEL (DESC(SET_DEBUG_LEVEL).optArg.argInt)
+#define VALUE_OPT_INTERACTIVE 'i'
+#define VALUE_OPT_NUMERIC 'n'
+#define VALUE_OPT_OLD_RV 0x1001
+#define VALUE_OPT_PEERS 'p'
+#define VALUE_OPT_REFID 'r'
+
+typedef enum {
+ REFID_HASH, REFID_IPV4
+} te_Refid;
+#define OPT_REFID_VAL2STR(_v) optionKeywordName(&DESC(REFID), (_v))
+#define OPT_VALUE_REFID (DESC(REFID).optArg.argEnum)
+#define VALUE_OPT_WIDE 'w'
+/** option flag (value) for help-value option */
+#define VALUE_OPT_HELP '?'
+/** option flag (value) for more-help-value option */
+#define VALUE_OPT_MORE_HELP '!'
+/** option flag (value) for version-value option */
+#define VALUE_OPT_VERSION 0x1002
+/** option flag (value) for save-opts-value option */
+#define VALUE_OPT_SAVE_OPTS '>'
+/** option flag (value) for load-opts-value option */
+#define VALUE_OPT_LOAD_OPTS '<'
+#define SET_OPT_SAVE_OPTS(a) STMTS( \
+ DESC(SAVE_OPTS).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(SAVE_OPTS).fOptState |= OPTST_SET; \
+ DESC(SAVE_OPTS).optArg.argString = (char const*)(a))
+/*
+ * Interface defines not associated with particular options
+ */
+#define ERRSKIP_OPTERR STMTS(ntpqOptions.fOptSet &= ~OPTPROC_ERRSTOP)
+#define ERRSTOP_OPTERR STMTS(ntpqOptions.fOptSet |= OPTPROC_ERRSTOP)
+#define RESTART_OPT(n) STMTS( \
+ ntpqOptions.curOptIdx = (n); \
+ ntpqOptions.pzCurOpt = NULL )
+#define START_OPT RESTART_OPT(1)
+#define USAGE(c) (*ntpqOptions.pUsageProc)(&ntpqOptions, c)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* * * * * *
+ *
+ * Declare the ntpq option descriptor.
+ */
+extern tOptions ntpqOptions;
+
+#if defined(ENABLE_NLS)
+# ifndef _
+# include <stdio.h>
+# ifndef HAVE_GETTEXT
+ extern char * gettext(char const *);
+# else
+# include <libintl.h>
+# endif
+
+# ifndef ATTRIBUTE_FORMAT_ARG
+# define ATTRIBUTE_FORMAT_ARG(_a)
+# endif
+
+static inline char* aoGetsText(char const* pz) ATTRIBUTE_FORMAT_ARG(1);
+static inline char* aoGetsText(char const* pz) {
+ if (pz == NULL) return NULL;
+ return (char*)gettext(pz);
+}
+# define _(s) aoGetsText(s)
+# endif /* _() */
+
+# define OPT_NO_XLAT_CFG_NAMES STMTS(ntpqOptions.fOptSet |= \
+ OPTPROC_NXLAT_OPT_CFG;)
+# define OPT_NO_XLAT_OPT_NAMES STMTS(ntpqOptions.fOptSet |= \
+ OPTPROC_NXLAT_OPT|OPTPROC_NXLAT_OPT_CFG;)
+
+# define OPT_XLAT_CFG_NAMES STMTS(ntpqOptions.fOptSet &= \
+ ~(OPTPROC_NXLAT_OPT|OPTPROC_NXLAT_OPT_CFG);)
+# define OPT_XLAT_OPT_NAMES STMTS(ntpqOptions.fOptSet &= \
+ ~OPTPROC_NXLAT_OPT;)
+
+#else /* ENABLE_NLS */
+# define OPT_NO_XLAT_CFG_NAMES
+# define OPT_NO_XLAT_OPT_NAMES
+
+# define OPT_XLAT_CFG_NAMES
+# define OPT_XLAT_OPT_NAMES
+
+# ifndef _
+# define _(_s) _s
+# endif
+#endif /* ENABLE_NLS */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* AUTOOPTS_NTPQ_OPTS_H_GUARD */
+
+/* ntpq-opts.h ends here */
diff --git a/bsd/freebsd/contrib/ntp/ntpq/ntpq-subs.c b/bsd/freebsd/contrib/ntp/ntpq/ntpq-subs.c
new file mode 100644
index 0000000..3020cc5
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/ntpq/ntpq-subs.c
@@ -0,0 +1,4142 @@
+/*
+ * ntpq-subs.c - subroutines which are called to perform ntpq commands.
+ */
+#include <config.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "ntpq.h"
+#include "ntpq-opts.h"
+
+extern char currenthost[];
+extern int currenthostisnum;
+size_t maxhostlen;
+
+/*
+ * Declarations for command handlers in here
+ */
+static associd_t checkassocid (u_int32);
+static struct varlist *findlistvar (struct varlist *, char *);
+static void doaddvlist (struct varlist *, const char *);
+static void dormvlist (struct varlist *, const char *);
+static void doclearvlist (struct varlist *);
+static void makequerydata (struct varlist *, size_t *, char *);
+static int doquerylist (struct varlist *, int, associd_t, int,
+ u_short *, size_t *, const char **);
+static void doprintvlist (struct varlist *, FILE *);
+static void addvars (struct parse *, FILE *);
+static void rmvars (struct parse *, FILE *);
+static void clearvars (struct parse *, FILE *);
+static void showvars (struct parse *, FILE *);
+static int dolist (struct varlist *, associd_t, int, int,
+ FILE *);
+static void readlist (struct parse *, FILE *);
+static void writelist (struct parse *, FILE *);
+static void readvar (struct parse *, FILE *);
+static void writevar (struct parse *, FILE *);
+static void clocklist (struct parse *, FILE *);
+static void clockvar (struct parse *, FILE *);
+static int findassidrange (u_int32, u_int32, int *, int *,
+ FILE *);
+static void mreadlist (struct parse *, FILE *);
+static void mreadvar (struct parse *, FILE *);
+static void printassoc (int, FILE *);
+static void associations (struct parse *, FILE *);
+static void lassociations (struct parse *, FILE *);
+static void passociations (struct parse *, FILE *);
+static void lpassociations (struct parse *, FILE *);
+
+#ifdef UNUSED
+static void radiostatus (struct parse *, FILE *);
+#endif /* UNUSED */
+
+static void authinfo (struct parse *, FILE *);
+static void pstats (struct parse *, FILE *);
+static long when (l_fp *, l_fp *, l_fp *);
+static char * prettyinterval (char *, size_t, long);
+static int doprintpeers (struct varlist *, int, int, size_t, const char *, FILE *, int);
+static int dogetpeers (struct varlist *, associd_t, FILE *, int);
+static void dopeers (int, FILE *, int);
+static void peers (struct parse *, FILE *);
+static void doapeers (int, FILE *, int);
+static void apeers (struct parse *, FILE *);
+static void lpeers (struct parse *, FILE *);
+static void doopeers (int, FILE *, int);
+static void opeers (struct parse *, FILE *);
+static void lopeers (struct parse *, FILE *);
+static void config (struct parse *, FILE *);
+static void saveconfig (struct parse *, FILE *);
+static void config_from_file(struct parse *, FILE *);
+static void mrulist (struct parse *, FILE *);
+static void ifstats (struct parse *, FILE *);
+static void reslist (struct parse *, FILE *);
+static void sysstats (struct parse *, FILE *);
+static void sysinfo (struct parse *, FILE *);
+static void kerninfo (struct parse *, FILE *);
+static void monstats (struct parse *, FILE *);
+static void iostats (struct parse *, FILE *);
+static void timerstats (struct parse *, FILE *);
+
+/*
+ * Commands we understand. Ntpdc imports this.
+ */
+struct xcmd opcmds[] = {
+ { "saveconfig", saveconfig, { NTP_STR, NO, NO, NO },
+ { "filename", "", "", ""},
+ "save ntpd configuration to file, . for current config file"},
+ { "associations", associations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print list of association ID's and statuses for the server's peers" },
+ { "passociations", passociations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print list of associations returned by last associations command" },
+ { "lassociations", lassociations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print list of associations including all client information" },
+ { "lpassociations", lpassociations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print last obtained list of associations, including client information" },
+ { "addvars", addvars, { NTP_STR, NO, NO, NO },
+ { "name[=value][,...]", "", "", "" },
+ "add variables to the variable list or change their values" },
+ { "rmvars", rmvars, { NTP_STR, NO, NO, NO },
+ { "name[,...]", "", "", "" },
+ "remove variables from the variable list" },
+ { "clearvars", clearvars, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "remove all variables from the variable list" },
+ { "showvars", showvars, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print variables on the variable list" },
+ { "readlist", readlist, { OPT|NTP_UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the system or peer variables included in the variable list" },
+ { "rl", readlist, { OPT|NTP_UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the system or peer variables included in the variable list" },
+ { "writelist", writelist, { OPT|NTP_UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "write the system or peer variables included in the variable list" },
+ { "readvar", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
+ { "assocID", "varname1", "varname2", "varname3" },
+ "read system or peer variables" },
+ { "rv", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
+ { "assocID", "varname1", "varname2", "varname3" },
+ "read system or peer variables" },
+ { "writevar", writevar, { NTP_UINT, NTP_STR, NO, NO },
+ { "assocID", "name=value,[...]", "", "" },
+ "write system or peer variables" },
+ { "mreadlist", mreadlist, { NTP_UINT, NTP_UINT, NO, NO },
+ { "assocIDlow", "assocIDhigh", "", "" },
+ "read the peer variables in the variable list for multiple peers" },
+ { "mrl", mreadlist, { NTP_UINT, NTP_UINT, NO, NO },
+ { "assocIDlow", "assocIDhigh", "", "" },
+ "read the peer variables in the variable list for multiple peers" },
+ { "mreadvar", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
+ { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
+ "read peer variables from multiple peers" },
+ { "mrv", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
+ { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
+ "read peer variables from multiple peers" },
+ { "clocklist", clocklist, { OPT|NTP_UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the clock variables included in the variable list" },
+ { "cl", clocklist, { OPT|NTP_UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the clock variables included in the variable list" },
+ { "clockvar", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read clock variables" },
+ { "cv", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read clock variables" },
+ { "pstats", pstats, { NTP_UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "show statistics for a peer" },
+ { "peers", peers, { OPT|IP_VERSION, NO, NO, NO },
+ { "-4|-6", "", "", "" },
+ "obtain and print a list of the server's peers [IP version]" },
+ { "apeers", apeers, { OPT|IP_VERSION, NO, NO, NO },
+ { "-4|-6", "", "", "" },
+ "obtain and print a list of the server's peers and their assocIDs [IP version]" },
+ { "lpeers", lpeers, { OPT|IP_VERSION, NO, NO, NO },
+ { "-4|-6", "", "", "" },
+ "obtain and print a list of all peers and clients [IP version]" },
+ { "opeers", opeers, { OPT|IP_VERSION, NO, NO, NO },
+ { "-4|-6", "", "", "" },
+ "print peer list the old way, with dstadr shown rather than refid [IP version]" },
+ { "lopeers", lopeers, { OPT|IP_VERSION, NO, NO, NO },
+ { "-4|-6", "", "", "" },
+ "obtain and print a list of all peers and clients showing dstadr [IP version]" },
+ { ":config", config, { NTP_STR, NO, NO, NO },
+ { "<configuration command line>", "", "", "" },
+ "send a remote configuration command to ntpd" },
+ { "config-from-file", config_from_file, { NTP_STR, NO, NO, NO },
+ { "<configuration filename>", "", "", "" },
+ "configure ntpd using the configuration filename" },
+ { "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
+ { "tag=value", "tag=value", "tag=value", "tag=value" },
+ "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." },
+ { "ifstats", ifstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "show statistics for each local address ntpd is using" },
+ { "reslist", reslist, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "show ntpd access control list" },
+ { "sysinfo", sysinfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display system summary" },
+ { "kerninfo", kerninfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display kernel loop and PPS statistics" },
+ { "sysstats", sysstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display system uptime and packet counts" },
+ { "monstats", monstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display monitor (mrulist) counters and limits" },
+ { "authinfo", authinfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display symmetric authentication counters" },
+ { "iostats", iostats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display network input and output counters" },
+ { "timerstats", timerstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display interval timer counters" },
+ { 0, 0, { NO, NO, NO, NO },
+ { "-4|-6", "", "", "" }, "" }
+};
+
+
+/*
+ * Variable list data space
+ */
+#define MAXLINE 512 /* maximum length of a line */
+#define MAXLIST 128 /* maximum variables in list */
+#define LENHOSTNAME 256 /* host name limit */
+
+#define MRU_GOT_COUNT 0x1
+#define MRU_GOT_LAST 0x2
+#define MRU_GOT_FIRST 0x4
+#define MRU_GOT_MV 0x8
+#define MRU_GOT_RS 0x10
+#define MRU_GOT_ADDR 0x20
+#define MRU_GOT_ALL (MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \
+ | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR)
+
+/*
+ * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two
+ */
+typedef enum mru_sort_order_tag {
+ MRUSORT_DEF = 0, /* lstint ascending */
+ MRUSORT_R_DEF, /* lstint descending */
+ MRUSORT_AVGINT, /* avgint ascending */
+ MRUSORT_R_AVGINT, /* avgint descending */
+ MRUSORT_ADDR, /* IPv4 asc. then IPv6 asc. */
+ MRUSORT_R_ADDR, /* IPv6 desc. then IPv4 desc. */
+ MRUSORT_COUNT, /* hit count ascending */
+ MRUSORT_R_COUNT, /* hit count descending */
+ MRUSORT_MAX, /* special: count of this enum */
+} mru_sort_order;
+
+const char * const mru_sort_keywords[MRUSORT_MAX] = {
+ "lstint", /* MRUSORT_DEF */
+ "-lstint", /* MRUSORT_R_DEF */
+ "avgint", /* MRUSORT_AVGINT */
+ "-avgint", /* MRUSORT_R_AVGINT */
+ "addr", /* MRUSORT_ADDR */
+ "-addr", /* MRUSORT_R_ADDR */
+ "count", /* MRUSORT_COUNT */
+ "-count", /* MRUSORT_R_COUNT */
+};
+
+typedef int (*qsort_cmp)(const void *, const void *);
+
+/*
+ * Old CTL_PST defines for version 2.
+ */
+#define OLD_CTL_PST_CONFIG 0x80
+#define OLD_CTL_PST_AUTHENABLE 0x40
+#define OLD_CTL_PST_AUTHENTIC 0x20
+#define OLD_CTL_PST_REACH 0x10
+#define OLD_CTL_PST_SANE 0x08
+#define OLD_CTL_PST_DISP 0x04
+
+#define OLD_CTL_PST_SEL_REJECT 0
+#define OLD_CTL_PST_SEL_SELCAND 1
+#define OLD_CTL_PST_SEL_SYNCCAND 2
+#define OLD_CTL_PST_SEL_SYSPEER 3
+
+char flash2[] = " .+* "; /* flash decode for version 2 */
+char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
+
+struct varlist {
+ const char *name;
+ char *value;
+} g_varlist[MAXLIST] = { { 0, 0 } };
+
+/*
+ * Imported from ntpq.c
+ */
+extern int showhostnames;
+extern int wideremote;
+extern int rawmode;
+extern struct servent *server_entry;
+extern struct association *assoc_cache;
+extern u_char pktversion;
+
+typedef struct mru_tag mru;
+struct mru_tag {
+ mru * hlink; /* next in hash table bucket */
+ DECL_DLIST_LINK(mru, mlink);
+ int count;
+ l_fp last;
+ l_fp first;
+ u_char mode;
+ u_char ver;
+ u_short rs;
+ sockaddr_u addr;
+};
+
+typedef struct ifstats_row_tag {
+ u_int ifnum;
+ sockaddr_u addr;
+ sockaddr_u bcast;
+ int enabled;
+ u_int flags;
+ u_int mcast_count;
+ char name[32];
+ u_int peer_count;
+ u_int received;
+ u_int sent;
+ u_int send_errors;
+ u_int ttl;
+ u_int uptime;
+} ifstats_row;
+
+typedef struct reslist_row_tag {
+ u_int idx;
+ sockaddr_u addr;
+ sockaddr_u mask;
+ u_long hits;
+ char flagstr[128];
+} reslist_row;
+
+typedef struct var_display_collection_tag {
+ const char * const tag; /* system variable */
+ const char * const display; /* descriptive text */
+ u_char type; /* NTP_STR, etc */
+ union {
+ char * str;
+ sockaddr_u sau; /* NTP_ADD */
+ l_fp lfp; /* NTP_LFP */
+ } v; /* retrieved value */
+} vdc;
+#if !defined(MISSING_C99_STRUCT_INIT)
+# define VDC_INIT(a, b, c) { .tag = a, .display = b, .type = c }
+#else
+# define VDC_INIT(a, b, c) { a, b, c }
+#endif
+/*
+ * other local function prototypes
+ */
+static int mrulist_ctrl_c_hook(void);
+static mru * add_mru(mru *);
+static int collect_mru_list(const char *, l_fp *);
+static int fetch_nonce(char *, size_t);
+static int qcmp_mru_avgint(const void *, const void *);
+static int qcmp_mru_r_avgint(const void *, const void *);
+static int qcmp_mru_addr(const void *, const void *);
+static int qcmp_mru_r_addr(const void *, const void *);
+static int qcmp_mru_count(const void *, const void *);
+static int qcmp_mru_r_count(const void *, const void *);
+static void validate_ifnum(FILE *, u_int, int *, ifstats_row *);
+static void another_ifstats_field(int *, ifstats_row *, FILE *);
+static void collect_display_vdc(associd_t as, vdc *table,
+ int decodestatus, FILE *fp);
+
+static int xprintf(FILE *, char const *, ...) NTP_PRINTF(2, 3);
+static int xputs(char const *, FILE *);
+static int xputc(int, FILE *);
+
+/*
+ * static globals
+ */
+static u_int mru_count;
+static u_int mru_dupes;
+volatile int mrulist_interrupted;
+static mru mru_list; /* listhead */
+static mru ** hash_table;
+
+/*
+ * qsort comparison function table for mrulist(). The first two
+ * entries are NULL because they are handled without qsort().
+ */
+static const qsort_cmp mru_qcmp_table[MRUSORT_MAX] = {
+ NULL, /* MRUSORT_DEF unused */
+ NULL, /* MRUSORT_R_DEF unused */
+ &qcmp_mru_avgint, /* MRUSORT_AVGINT */
+ &qcmp_mru_r_avgint, /* MRUSORT_R_AVGINT */
+ &qcmp_mru_addr, /* MRUSORT_ADDR */
+ &qcmp_mru_r_addr, /* MRUSORT_R_ADDR */
+ &qcmp_mru_count, /* MRUSORT_COUNT */
+ &qcmp_mru_r_count, /* MRUSORT_R_COUNT */
+};
+
+/*
+ * NULL-pointer safe FILE I/O: use stderr if no file supplied.
+ */
+static int
+xprintf(
+ FILE * ofp,
+ char const * fmt,
+ ...
+ )
+{
+ va_list va;
+ int rc;
+
+ va_start(va, fmt);
+ rc = vfprintf((ofp ? ofp : stderr), fmt, va);
+ va_end(va);
+ return rc;
+}
+
+static int
+xputs(
+ char const * str,
+ FILE * ofp
+ )
+{
+ return fputs(str, (ofp ? ofp : stderr));
+}
+
+static int
+xputc(
+ int ch,
+ FILE * ofp
+ )
+{
+ return fputc(ch, (ofp ? ofp : stderr));
+}
+
+/*
+ * checkassocid - return the association ID, checking to see if it is valid
+ */
+static associd_t
+checkassocid(
+ u_int32 value
+ )
+{
+ associd_t associd;
+ u_long ulvalue;
+
+ associd = (associd_t)value;
+ if (0 == associd || value != associd) {
+ ulvalue = value;
+ xprintf(stderr,
+ "***Invalid association ID %lu specified\n",
+ ulvalue);
+ return 0;
+ }
+
+ return associd;
+}
+
+
+/*
+ * findlistvar - Look for the named variable in a varlist. If found,
+ * return a pointer to it. Otherwise, if the list has
+ * slots available, return the pointer to the first free
+ * slot, or NULL if it's full.
+ */
+static struct varlist *
+findlistvar(
+ struct varlist *list,
+ char *name
+ )
+{
+ struct varlist *vl;
+
+ for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++)
+ if (!strcmp(name, vl->name))
+ return vl;
+ if (vl < list + MAXLIST)
+ return vl;
+
+ return NULL;
+}
+
+
+/*
+ * doaddvlist - add variable(s) to the variable list
+ */
+static void
+doaddvlist(
+ struct varlist *vlist,
+ const char *vars
+ )
+{
+ struct varlist *vl;
+ size_t len;
+ char *name;
+ char *value;
+
+ len = strlen(vars);
+ while (nextvar(&len, &vars, &name, &value)) {
+ INSIST(name && value);
+ vl = findlistvar(vlist, name);
+ if (NULL == vl) {
+ xprintf(stderr, "Variable list full\n");
+ return;
+ }
+
+ if (NULL == vl->name) {
+ vl->name = estrdup(name);
+ } else if (vl->value != NULL) {
+ free(vl->value);
+ vl->value = NULL;
+ }
+
+ if (value != NULL)
+ vl->value = estrdup(value);
+ }
+}
+
+
+/*
+ * dormvlist - remove variable(s) from the variable list
+ */
+static void
+dormvlist(
+ struct varlist *vlist,
+ const char *vars
+ )
+{
+ struct varlist *vl;
+ size_t len;
+ char *name;
+ char *value;
+
+ len = strlen(vars);
+ while (nextvar(&len, &vars, &name, &value)) {
+ INSIST(name && value);
+ vl = findlistvar(vlist, name);
+ if (vl == 0 || vl->name == 0) {
+ (void) xprintf(stderr, "Variable `%s' not found\n",
+ name);
+ } else {
+ free((void *)(intptr_t)vl->name);
+ if (vl->value != 0)
+ free(vl->value);
+ for ( ; (vl+1) < (g_varlist + MAXLIST)
+ && (vl+1)->name != 0; vl++) {
+ vl->name = (vl+1)->name;
+ vl->value = (vl+1)->value;
+ }
+ vl->name = vl->value = 0;
+ }
+ }
+}
+
+
+/*
+ * doclearvlist - clear a variable list
+ */
+static void
+doclearvlist(
+ struct varlist *vlist
+ )
+{
+ register struct varlist *vl;
+
+ for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
+ free((void *)(intptr_t)vl->name);
+ vl->name = 0;
+ if (vl->value != 0) {
+ free(vl->value);
+ vl->value = 0;
+ }
+ }
+}
+
+
+/*
+ * makequerydata - form a data buffer to be included with a query
+ */
+static void
+makequerydata(
+ struct varlist *vlist,
+ size_t *datalen,
+ char *data
+ )
+{
+ register struct varlist *vl;
+ register char *cp, *cpend;
+ register size_t namelen, valuelen;
+ register size_t totallen;
+
+ cp = data;
+ cpend = data + *datalen;
+
+ for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
+ namelen = strlen(vl->name);
+ if (vl->value == 0)
+ valuelen = 0;
+ else
+ valuelen = strlen(vl->value);
+ totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
+ if (cp + totallen > cpend) {
+ xprintf(stderr,
+ "***Ignoring variables starting with `%s'\n",
+ vl->name);
+ break;
+ }
+
+ if (cp != data)
+ *cp++ = ',';
+ memcpy(cp, vl->name, (size_t)namelen);
+ cp += namelen;
+ if (valuelen != 0) {
+ *cp++ = '=';
+ memcpy(cp, vl->value, (size_t)valuelen);
+ cp += valuelen;
+ }
+ }
+ *datalen = (size_t)(cp - data);
+}
+
+
+/*
+ * doquerylist - send a message including variables in a list
+ */
+static int
+doquerylist(
+ struct varlist *vlist,
+ int op,
+ associd_t associd,
+ int auth,
+ u_short *rstatus,
+ size_t *dsize,
+ const char **datap
+ )
+{
+ char data[CTL_MAX_DATA_LEN];
+ size_t datalen;
+
+ datalen = sizeof(data);
+ makequerydata(vlist, &datalen, data);
+
+ return doquery(op, associd, auth, datalen, data, rstatus, dsize,
+ datap);
+}
+
+
+/*
+ * doprintvlist - print the variables on a list
+ */
+static void
+doprintvlist(
+ struct varlist *vlist,
+ FILE *fp
+ )
+{
+ size_t n;
+
+ if (NULL == vlist->name) {
+ xprintf(fp, "No variables on list\n");
+ return;
+ }
+ for (n = 0; n < MAXLIST && vlist[n].name != NULL; n++) {
+ if (NULL == vlist[n].value)
+ xprintf(fp, "%s\n", vlist[n].name);
+ else
+ xprintf(fp, "%s=%s\n", vlist[n].name,
+ vlist[n].value);
+ }
+}
+
+/*
+ * addvars - add variables to the variable list
+ */
+/*ARGSUSED*/
+static void
+addvars(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ doaddvlist(g_varlist, pcmd->argval[0].string);
+}
+
+
+/*
+ * rmvars - remove variables from the variable list
+ */
+/*ARGSUSED*/
+static void
+rmvars(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ dormvlist(g_varlist, pcmd->argval[0].string);
+}
+
+
+/*
+ * clearvars - clear the variable list
+ */
+/*ARGSUSED*/
+static void
+clearvars(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ doclearvlist(g_varlist);
+}
+
+
+/*
+ * showvars - show variables on the variable list
+ */
+/*ARGSUSED*/
+static void
+showvars(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ doprintvlist(g_varlist, fp);
+}
+
+
+/*
+ * dolist - send a request with the given list of variables
+ */
+static int
+dolist(
+ struct varlist *vlist,
+ associd_t associd,
+ int op,
+ int type,
+ FILE *fp
+ )
+{
+ const char *datap;
+ int res;
+ size_t dsize;
+ u_short rstatus;
+ int quiet;
+
+ /*
+ * if we're asking for specific variables don't include the
+ * status header line in the output.
+ */
+ if (old_rv)
+ quiet = 0;
+ else
+ quiet = (vlist->name != NULL);
+
+ res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
+
+ if (res != 0)
+ return 0;
+
+ if (numhosts > 1)
+ xprintf(fp, "server=%s ", currenthost);
+ if (dsize == 0) {
+ if (associd == 0)
+ xprintf(fp, "No system%s variables returned\n",
+ (type == TYPE_CLOCK) ? " clock" : "");
+ else
+ xprintf(fp,
+ "No information returned for%s association %u\n",
+ (type == TYPE_CLOCK) ? " clock" : "",
+ associd);
+ return 1;
+ }
+
+ if (!quiet)
+ xprintf(fp, "associd=%u ", associd);
+ printvars(dsize, datap, (int)rstatus, type, quiet, fp);
+ return 1;
+}
+
+
+/*
+ * readlist - send a read variables request with the variables on the list
+ */
+static void
+readlist(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ associd_t associd;
+ int type;
+
+ if (pcmd->nargs == 0) {
+ associd = 0;
+ } else {
+ /* HMS: I think we want the u_int32 target here, not the u_long */
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+ }
+
+ type = (0 == associd)
+ ? TYPE_SYS
+ : TYPE_PEER;
+ dolist(g_varlist, associd, CTL_OP_READVAR, type, fp);
+}
+
+
+/*
+ * writelist - send a write variables request with the variables on the list
+ */
+static void
+writelist(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ const char *datap;
+ int res;
+ associd_t associd;
+ size_t dsize;
+ u_short rstatus;
+
+ if (pcmd->nargs == 0) {
+ associd = 0;
+ } else {
+ /* HMS: Do we really want uval here? */
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+ }
+
+ res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return;
+
+ if (numhosts > 1)
+ (void) xprintf(fp, "server=%s ", currenthost);
+ if (dsize == 0)
+ (void) xprintf(fp, "done! (no data returned)\n");
+ else {
+ (void) xprintf(fp,"associd=%u ", associd);
+ printvars(dsize, datap, (int)rstatus,
+ (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp);
+ }
+ return;
+}
+
+
+/*
+ * readvar - send a read variables request with the specified variables
+ */
+static void
+readvar(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ associd_t associd;
+ size_t tmpcount;
+ size_t u;
+ int type;
+ struct varlist tmplist[MAXLIST];
+
+
+ /* HMS: uval? */
+ if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ ZERO(tmplist);
+ if (pcmd->nargs > 1) {
+ tmpcount = pcmd->nargs - 1;
+ for (u = 0; u < tmpcount; u++)
+ doaddvlist(tmplist, pcmd->argval[1 + u].string);
+ }
+
+ type = (0 == associd)
+ ? TYPE_SYS
+ : TYPE_PEER;
+ dolist(tmplist, associd, CTL_OP_READVAR, type, fp);
+
+ doclearvlist(tmplist);
+}
+
+
+/*
+ * writevar - send a write variables request with the specified variables
+ */
+static void
+writevar(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ const char *datap;
+ int res;
+ associd_t associd;
+ int type;
+ size_t dsize;
+ u_short rstatus;
+ struct varlist tmplist[MAXLIST];
+
+ /* HMS: uval? */
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ ZERO(tmplist);
+ doaddvlist(tmplist, pcmd->argval[1].string);
+
+ res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
+ &dsize, &datap);
+
+ doclearvlist(tmplist);
+
+ if (res != 0)
+ return;
+
+ if (numhosts > 1)
+ xprintf(fp, "server=%s ", currenthost);
+ if (dsize == 0)
+ xprintf(fp, "done! (no data returned)\n");
+ else {
+ xprintf(fp,"associd=%u ", associd);
+ type = (0 == associd)
+ ? TYPE_SYS
+ : TYPE_PEER;
+ printvars(dsize, datap, (int)rstatus, type, 0, fp);
+ }
+ return;
+}
+
+
+/*
+ * clocklist - send a clock variables request with the variables on the list
+ */
+static void
+clocklist(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ associd_t associd;
+
+ /* HMS: uval? */
+ if (pcmd->nargs == 0) {
+ associd = 0;
+ } else {
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+ }
+
+ dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
+}
+
+
+/*
+ * clockvar - send a clock variables request with the specified variables
+ */
+static void
+clockvar(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ associd_t associd;
+ struct varlist tmplist[MAXLIST];
+
+ /* HMS: uval? */
+ if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ ZERO(tmplist);
+ if (pcmd->nargs >= 2)
+ doaddvlist(tmplist, pcmd->argval[1].string);
+
+ dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
+
+ doclearvlist(tmplist);
+}
+
+
+/*
+ * findassidrange - verify a range of association ID's
+ */
+static int
+findassidrange(
+ u_int32 assid1,
+ u_int32 assid2,
+ int * from,
+ int * to,
+ FILE * fp
+ )
+{
+ associd_t assids[2];
+ int ind[COUNTOF(assids)];
+ u_int i;
+ size_t a;
+
+
+ if (0 == numassoc)
+ dogetassoc(fp);
+
+ assids[0] = checkassocid(assid1);
+ if (0 == assids[0])
+ return 0;
+ assids[1] = checkassocid(assid2);
+ if (0 == assids[1])
+ return 0;
+
+ for (a = 0; a < COUNTOF(assids); a++) {
+ ind[a] = -1;
+ for (i = 0; i < numassoc; i++)
+ if (assoc_cache[i].assid == assids[a])
+ ind[a] = i;
+ }
+ for (a = 0; a < COUNTOF(assids); a++)
+ if (-1 == ind[a]) {
+ xprintf(stderr,
+ "***Association ID %u not found in list\n",
+ assids[a]);
+ return 0;
+ }
+
+ if (ind[0] < ind[1]) {
+ *from = ind[0];
+ *to = ind[1];
+ } else {
+ *to = ind[0];
+ *from = ind[1];
+ }
+ return 1;
+}
+
+
+
+/*
+ * mreadlist - send a read variables request for multiple associations
+ */
+static void
+mreadlist(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ int i;
+ int from;
+ int to;
+
+ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
+ &from, &to, fp))
+ return;
+
+ for (i = from; i <= to; i++) {
+ if (i != from)
+ xprintf(fp, "\n");
+ if (!dolist(g_varlist, assoc_cache[i].assid,
+ CTL_OP_READVAR, TYPE_PEER, fp))
+ return;
+ }
+ return;
+}
+
+
+/*
+ * mreadvar - send a read variables request for multiple associations
+ */
+static void
+mreadvar(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ int i;
+ int from;
+ int to;
+ struct varlist tmplist[MAXLIST];
+ struct varlist *pvars;
+
+ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
+ &from, &to, fp))
+ return;
+
+ ZERO(tmplist);
+ if (pcmd->nargs >= 3) {
+ doaddvlist(tmplist, pcmd->argval[2].string);
+ pvars = tmplist;
+ } else {
+ pvars = g_varlist;
+ }
+
+ for (i = from; i <= to; i++) {
+ if (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR,
+ TYPE_PEER, fp))
+ break;
+ }
+
+ if (pvars == tmplist)
+ doclearvlist(tmplist);
+
+ return;
+}
+
+
+/*
+ * dogetassoc - query the host for its list of associations
+ */
+int
+dogetassoc(
+ FILE *fp
+ )
+{
+ const char *datap;
+ const u_short *pus;
+ int res;
+ size_t dsize;
+ u_short rstatus;
+
+ res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ if (numhosts > 1)
+ xprintf(fp, "server=%s ", currenthost);
+ xprintf(fp, "No association ID's returned\n");
+ return 0;
+ }
+
+ if (dsize & 0x3) {
+ if (numhosts > 1)
+ xprintf(stderr, "server=%s ", currenthost);
+ xprintf(stderr,
+ "***Server returned %zu octets, should be multiple of 4\n",
+ dsize);
+ return 0;
+ }
+
+ numassoc = 0;
+
+ while (dsize > 0) {
+ if (numassoc >= assoc_cache_slots) {
+ grow_assoc_cache();
+ }
+ pus = (const void *)datap;
+ assoc_cache[numassoc].assid = ntohs(*pus);
+ datap += sizeof(*pus);
+ pus = (const void *)datap;
+ assoc_cache[numassoc].status = ntohs(*pus);
+ datap += sizeof(*pus);
+ dsize -= 2 * sizeof(*pus);
+ if (debug) {
+ xprintf(stderr, "[%u] ",
+ assoc_cache[numassoc].assid);
+ }
+ numassoc++;
+ }
+ if (debug) {
+ xprintf(stderr, "\n%d associations total\n", numassoc);
+ }
+ sortassoc();
+ return 1;
+}
+
+
+/*
+ * printassoc - print the current list of associations
+ */
+static void
+printassoc(
+ int showall,
+ FILE *fp
+ )
+{
+ register char *bp;
+ u_int i;
+ u_char statval;
+ int event;
+ u_long event_count;
+ const char *conf;
+ const char *reach;
+ const char *auth;
+ const char *condition = "";
+ const char *last_event;
+ char buf[128];
+
+ if (numassoc == 0) {
+ (void) xprintf(fp, "No association ID's in list\n");
+ return;
+ }
+
+ /*
+ * Output a header
+ */
+ (void) xprintf(fp,
+ "ind assid status conf reach auth condition last_event cnt\n");
+ (void) xprintf(fp,
+ "===========================================================\n");
+ for (i = 0; i < numassoc; i++) {
+ statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status);
+ if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
+ continue;
+ event = CTL_PEER_EVENT(assoc_cache[i].status);
+ event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
+ if (statval & CTL_PST_CONFIG)
+ conf = "yes";
+ else
+ conf = "no";
+ if (statval & CTL_PST_BCAST) {
+ reach = "none";
+ if (statval & CTL_PST_AUTHENABLE)
+ auth = "yes";
+ else
+ auth = "none";
+ } else {
+ if (statval & CTL_PST_REACH)
+ reach = "yes";
+ else
+ reach = "no";
+ if (statval & CTL_PST_AUTHENABLE) {
+ if (statval & CTL_PST_AUTHENTIC)
+ auth = "ok ";
+ else
+ auth = "bad";
+ } else {
+ auth = "none";
+ }
+ }
+ if (pktversion > NTP_OLDVERSION) {
+ switch (statval & 0x7) {
+
+ case CTL_PST_SEL_REJECT:
+ condition = "reject";
+ break;
+
+ case CTL_PST_SEL_SANE:
+ condition = "falsetick";
+ break;
+
+ case CTL_PST_SEL_CORRECT:
+ condition = "excess";
+ break;
+
+ case CTL_PST_SEL_SELCAND:
+ condition = "outlier";
+ break;
+
+ case CTL_PST_SEL_SYNCCAND:
+ condition = "candidate";
+ break;
+
+ case CTL_PST_SEL_EXCESS:
+ condition = "backup";
+ break;
+
+ case CTL_PST_SEL_SYSPEER:
+ condition = "sys.peer";
+ break;
+
+ case CTL_PST_SEL_PPS:
+ condition = "pps.peer";
+ break;
+ }
+ } else {
+ switch (statval & 0x3) {
+
+ case OLD_CTL_PST_SEL_REJECT:
+ if (!(statval & OLD_CTL_PST_SANE))
+ condition = "insane";
+ else if (!(statval & OLD_CTL_PST_DISP))
+ condition = "hi_disp";
+ else
+ condition = "";
+ break;
+
+ case OLD_CTL_PST_SEL_SELCAND:
+ condition = "sel_cand";
+ break;
+
+ case OLD_CTL_PST_SEL_SYNCCAND:
+ condition = "sync_cand";
+ break;
+
+ case OLD_CTL_PST_SEL_SYSPEER:
+ condition = "sys_peer";
+ break;
+ }
+ }
+ switch (PEER_EVENT|event) {
+
+ case PEVNT_MOBIL:
+ last_event = "mobilize";
+ break;
+
+ case PEVNT_DEMOBIL:
+ last_event = "demobilize";
+ break;
+
+ case PEVNT_REACH:
+ last_event = "reachable";
+ break;
+
+ case PEVNT_UNREACH:
+ last_event = "unreachable";
+ break;
+
+ case PEVNT_RESTART:
+ last_event = "restart";
+ break;
+
+ case PEVNT_REPLY:
+ last_event = "no_reply";
+ break;
+
+ case PEVNT_RATE:
+ last_event = "rate_exceeded";
+ break;
+
+ case PEVNT_DENY:
+ last_event = "access_denied";
+ break;
+
+ case PEVNT_ARMED:
+ last_event = "leap_armed";
+ break;
+
+ case PEVNT_NEWPEER:
+ last_event = "sys_peer";
+ break;
+
+ case PEVNT_CLOCK:
+ last_event = "clock_alarm";
+ break;
+
+ default:
+ last_event = "";
+ break;
+ }
+ snprintf(buf, sizeof(buf),
+ "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2lu",
+ i + 1, assoc_cache[i].assid,
+ assoc_cache[i].status, conf, reach, auth,
+ condition, last_event, event_count);
+ bp = buf + strlen(buf);
+ while (bp > buf && ' ' == bp[-1])
+ --bp;
+ bp[0] = '\0';
+ xprintf(fp, "%s\n", buf);
+ }
+}
+
+
+/*
+ * associations - get, record and print a list of associations
+ */
+/*ARGSUSED*/
+static void
+associations(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (dogetassoc(fp))
+ printassoc(0, fp);
+}
+
+
+/*
+ * lassociations - get, record and print a long list of associations
+ */
+/*ARGSUSED*/
+static void
+lassociations(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (dogetassoc(fp))
+ printassoc(1, fp);
+}
+
+
+/*
+ * passociations - print the association list
+ */
+/*ARGSUSED*/
+static void
+passociations(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ printassoc(0, fp);
+}
+
+
+/*
+ * lpassociations - print the long association list
+ */
+/*ARGSUSED*/
+static void
+lpassociations(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ printassoc(1, fp);
+}
+
+
+/*
+ * saveconfig - dump ntp server configuration to server file
+ */
+static void
+saveconfig(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ const char *datap;
+ int res;
+ size_t dsize;
+ u_short rstatus;
+
+ if (0 == pcmd->nargs)
+ return;
+
+ res = doquery(CTL_OP_SAVECONFIG, 0, 1,
+ strlen(pcmd->argval[0].string),
+ pcmd->argval[0].string, &rstatus, &dsize,
+ &datap);
+
+ if (res != 0)
+ return;
+
+ if (0 == dsize)
+ xprintf(fp, "(no response message, curiously)");
+ else
+ xprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */
+}
+
+
+#ifdef UNUSED
+/*
+ * radiostatus - print the radio status returned by the server
+ */
+/*ARGSUSED*/
+static void
+radiostatus(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ char *datap;
+ int res;
+ int dsize;
+ u_short rstatus;
+
+ res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return;
+
+ if (numhosts > 1)
+ (void) xprintf(fp, "server=%s ", currenthost);
+ if (dsize == 0) {
+ (void) xprintf(fp, "No radio status string returned\n");
+ return;
+ }
+
+ asciize(dsize, datap, fp);
+}
+#endif /* UNUSED */
+
+/*
+ * when - print how long its been since his last packet arrived
+ */
+static long
+when(
+ l_fp *ts,
+ l_fp *rec,
+ l_fp *reftime
+ )
+{
+ l_fp *lasttime;
+
+ if (rec->l_ui != 0)
+ lasttime = rec;
+ else if (reftime->l_ui != 0)
+ lasttime = reftime;
+ else
+ return 0;
+
+ if (ts->l_ui < lasttime->l_ui)
+ return -1;
+ return (ts->l_ui - lasttime->l_ui);
+}
+
+
+/*
+ * Pretty-print an interval into the given buffer, in a human-friendly format.
+ */
+static char *
+prettyinterval(
+ char *buf,
+ size_t cb,
+ long diff
+ )
+{
+ if (diff <= 0) {
+ buf[0] = '-';
+ buf[1] = 0;
+ return buf;
+ }
+
+ if (diff <= 2048) {
+ snprintf(buf, cb, "%u", (unsigned int)diff);
+ return buf;
+ }
+
+ diff = (diff + 29) / 60;
+ if (diff <= 300) {
+ snprintf(buf, cb, "%um", (unsigned int)diff);
+ return buf;
+ }
+
+ diff = (diff + 29) / 60;
+ if (diff <= 96) {
+ snprintf(buf, cb, "%uh", (unsigned int)diff);
+ return buf;
+ }
+
+ diff = (diff + 11) / 24;
+ if (diff <= 999) {
+ snprintf(buf, cb, "%ud", (unsigned int)diff);
+ return buf;
+ }
+
+ /* years are only approximated... */
+ diff = (long)floor(diff / 365.25 + 0.5);
+ if (diff <= 999) {
+ snprintf(buf, cb, "%uy", (unsigned int)diff);
+ return buf;
+ }
+ /* Ok, this amounts to infinity... */
+ strlcpy(buf, "INF", cb);
+ return buf;
+}
+
+static char
+decodeaddrtype(
+ sockaddr_u *sock
+ )
+{
+ char ch = '-';
+ u_int32 dummy;
+
+ switch(AF(sock)) {
+ case AF_INET:
+ dummy = SRCADR(sock);
+ ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' :
+ ((dummy&0x000000ff)==0x000000ff) ? 'b' :
+ ((dummy&0xffffffff)==0x7f000001) ? 'l' :
+ ((dummy&0xffffffe0)==0x00000000) ? '-' :
+ 'u');
+ break;
+ case AF_INET6:
+ if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock)))
+ ch = 'm';
+ else
+ ch = 'u';
+ break;
+ default:
+ ch = '-';
+ break;
+ }
+ return ch;
+}
+
+/*
+ * A list of variables required by the peers command
+ */
+struct varlist opeervarlist[] = {
+ { "srcadr", 0 }, /* 0 */
+ { "dstadr", 0 }, /* 1 */
+ { "stratum", 0 }, /* 2 */
+ { "hpoll", 0 }, /* 3 */
+ { "ppoll", 0 }, /* 4 */
+ { "reach", 0 }, /* 5 */
+ { "delay", 0 }, /* 6 */
+ { "offset", 0 }, /* 7 */
+ { "jitter", 0 }, /* 8 */
+ { "dispersion", 0 }, /* 9 */
+ { "rec", 0 }, /* 10 */
+ { "reftime", 0 }, /* 11 */
+ { "srcport", 0 }, /* 12 */
+ { "hmode", 0 }, /* 13 */
+ { 0, 0 }
+};
+
+struct varlist peervarlist[] = {
+ { "srcadr", 0 }, /* 0 */
+ { "refid", 0 }, /* 1 */
+ { "stratum", 0 }, /* 2 */
+ { "hpoll", 0 }, /* 3 */
+ { "ppoll", 0 }, /* 4 */
+ { "reach", 0 }, /* 5 */
+ { "delay", 0 }, /* 6 */
+ { "offset", 0 }, /* 7 */
+ { "jitter", 0 }, /* 8 */
+ { "dispersion", 0 }, /* 9 */
+ { "rec", 0 }, /* 10 */
+ { "reftime", 0 }, /* 11 */
+ { "srcport", 0 }, /* 12 */
+ { "hmode", 0 }, /* 13 */
+ { "srchost", 0 }, /* 14 */
+ { 0, 0 }
+};
+
+struct varlist apeervarlist[] = {
+ { "srcadr", 0 }, /* 0 */
+ { "refid", 0 }, /* 1 */
+ { "assid", 0 }, /* 2 */
+ { "stratum", 0 }, /* 3 */
+ { "hpoll", 0 }, /* 4 */
+ { "ppoll", 0 }, /* 5 */
+ { "reach", 0 }, /* 6 */
+ { "delay", 0 }, /* 7 */
+ { "offset", 0 }, /* 8 */
+ { "jitter", 0 }, /* 9 */
+ { "dispersion", 0 }, /* 10 */
+ { "rec", 0 }, /* 11 */
+ { "reftime", 0 }, /* 12 */
+ { "srcport", 0 }, /* 13 */
+ { "hmode", 0 }, /* 14 */
+ { "srchost", 0 }, /* 15 */
+ { 0, 0 }
+};
+
+
+/*
+ * Decode an incoming data buffer and print a line in the peer list
+ */
+static int
+doprintpeers(
+ struct varlist *pvl,
+ int associd,
+ int rstatus,
+ size_t datalen,
+ const char *data,
+ FILE *fp,
+ int af
+ )
+{
+ char *name;
+ char *value = NULL;
+ int c;
+ size_t len;
+ int have_srchost;
+ int have_dstadr;
+ int have_da_rid;
+ int have_jitter;
+ sockaddr_u srcadr;
+ sockaddr_u dstadr;
+ sockaddr_u dum_store;
+ sockaddr_u refidadr;
+ long hmode = 0;
+ u_long srcport = 0;
+ u_int32 u32;
+ const char *dstadr_refid = "0.0.0.0";
+ const char *serverlocal;
+ size_t drlen;
+ u_long stratum = 0;
+ long ppoll = 0;
+ long hpoll = 0;
+ u_long reach = 0;
+ l_fp estoffset;
+ l_fp estdelay;
+ l_fp estjitter;
+ l_fp estdisp;
+ l_fp reftime;
+ l_fp rec;
+ l_fp ts;
+ u_long poll_sec;
+ u_long flash = 0;
+ char type = '?';
+ char clock_name[LENHOSTNAME];
+ char whenbuf[12], pollbuf[12];
+ /* [Bug 3482] formally whenbuf & pollbuf should be able to hold
+ * a full signed int. Not that we would use that much string
+ * data for it...
+ */
+ get_systime(&ts);
+
+ have_srchost = FALSE;
+ have_dstadr = FALSE;
+ have_da_rid = FALSE;
+ have_jitter = FALSE;
+ ZERO_SOCK(&srcadr);
+ ZERO_SOCK(&dstadr);
+ clock_name[0] = '\0';
+ ZERO(estoffset);
+ ZERO(estdelay);
+ ZERO(estjitter);
+ ZERO(estdisp);
+
+ while (nextvar(&datalen, &data, &name, &value)) {
+ INSIST(name && value);
+ if (!strcmp("srcadr", name) ||
+ !strcmp("peeradr", name)) {
+ if (!decodenetnum(value, &srcadr))
+ xprintf(stderr, "malformed %s=%s\n",
+ name, value);
+ } else if (!strcmp("srchost", name)) {
+ if (pvl == peervarlist || pvl == apeervarlist) {
+ len = strlen(value);
+ if (2 < len &&
+ (size_t)len < sizeof(clock_name)) {
+ /* strip quotes */
+ value++;
+ len -= 2;
+ memcpy(clock_name, value, len);
+ clock_name[len] = '\0';
+ have_srchost = TRUE;
+ }
+ }
+ } else if (!strcmp("dstadr", name)) {
+ if (decodenetnum(value, &dum_store)) {
+ type = decodeaddrtype(&dum_store);
+ have_dstadr = TRUE;
+ dstadr = dum_store;
+ if (pvl == opeervarlist) {
+ have_da_rid = TRUE;
+ dstadr_refid = trunc_left(stoa(&dstadr), 15);
+ }
+ }
+ } else if (!strcmp("hmode", name)) {
+ decodeint(value, &hmode);
+ } else if (!strcmp("refid", name)) {
+ if ( (pvl == peervarlist)
+ && (drefid == REFID_IPV4)) {
+ have_da_rid = TRUE;
+ drlen = strlen(value);
+ if (0 == drlen) {
+ dstadr_refid = "";
+ } else if (drlen <= 4) {
+ ZERO(u32);
+ memcpy(&u32, value, drlen);
+ dstadr_refid = refid_str(u32, 1);
+ } else if (decodenetnum(value, &refidadr)) {
+ if (SOCK_UNSPEC(&refidadr))
+ dstadr_refid = "0.0.0.0";
+ else if (ISREFCLOCKADR(&refidadr))
+ dstadr_refid =
+ refnumtoa(&refidadr);
+ else
+ dstadr_refid =
+ stoa(&refidadr);
+ } else {
+ have_da_rid = FALSE;
+ }
+ } else if ( (pvl == apeervarlist)
+ || (pvl == peervarlist)) {
+ /* no need to check drefid == REFID_HASH */
+ have_da_rid = TRUE;
+ drlen = strlen(value);
+ if (0 == drlen) {
+ dstadr_refid = "";
+ } else if (drlen <= 4) {
+ ZERO(u32);
+ memcpy(&u32, value, drlen);
+ dstadr_refid = refid_str(u32, 1);
+ //xprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value);
+ } else if (decodenetnum(value, &refidadr)) {
+ if (SOCK_UNSPEC(&refidadr))
+ dstadr_refid = "0.0.0.0";
+ else if (ISREFCLOCKADR(&refidadr))
+ dstadr_refid =
+ refnumtoa(&refidadr);
+ else {
+ char *buf = emalloc(10);
+ int i = ntohl(refidadr.sa4.sin_addr.s_addr);
+
+ snprintf(buf, 10,
+ "%0x", i);
+ dstadr_refid = buf;
+ //xprintf(stderr, "apeervarlist refid: value=<%x>\n", i);
+ }
+ //xprintf(stderr, "apeervarlist refid: value=<%s>\n", value);
+ } else {
+ have_da_rid = FALSE;
+ }
+ }
+ } else if (!strcmp("stratum", name)) {
+ decodeuint(value, &stratum);
+ } else if (!strcmp("hpoll", name)) {
+ if (decodeint(value, &hpoll) && hpoll < 0)
+ hpoll = NTP_MINPOLL;
+ } else if (!strcmp("ppoll", name)) {
+ if (decodeint(value, &ppoll) && ppoll < 0)
+ ppoll = NTP_MINPOLL;
+ } else if (!strcmp("reach", name)) {
+ decodeuint(value, &reach);
+ } else if (!strcmp("delay", name)) {
+ decodetime(value, &estdelay);
+ } else if (!strcmp("offset", name)) {
+ decodetime(value, &estoffset);
+ } else if (!strcmp("jitter", name)) {
+ if ((pvl == peervarlist || pvl == apeervarlist)
+ && decodetime(value, &estjitter))
+ have_jitter = 1;
+ } else if (!strcmp("rootdisp", name) ||
+ !strcmp("dispersion", name)) {
+ decodetime(value, &estdisp);
+ } else if (!strcmp("rec", name)) {
+ decodets(value, &rec);
+ } else if (!strcmp("srcport", name) ||
+ !strcmp("peerport", name)) {
+ decodeuint(value, &srcport);
+ } else if (!strcmp("reftime", name)) {
+ if (!decodets(value, &reftime))
+ L_CLR(&reftime);
+ } else if (!strcmp("flash", name)) {
+ decodeuint(value, &flash);
+ } else {
+ // xprintf(stderr, "UNRECOGNIZED name=%s ", name);
+ }
+ }
+
+ /*
+ * hmode gives the best guidance for the t column. If the response
+ * did not include hmode we'll use the old decodeaddrtype() result.
+ */
+ switch (hmode) {
+
+ case MODE_BCLIENT:
+ /* broadcastclient or multicastclient */
+ type = 'b';
+ break;
+
+ case MODE_BROADCAST:
+ /* broadcast or multicast server */
+ if (IS_MCAST(&srcadr))
+ type = 'M';
+ else
+ type = 'B';
+ break;
+
+ case MODE_CLIENT:
+ if (ISREFCLOCKADR(&srcadr))
+ type = 'l'; /* local refclock*/
+ else if (SOCK_UNSPEC(&srcadr))
+ type = 'p'; /* pool */
+ else if (IS_MCAST(&srcadr))
+ type = 'a'; /* manycastclient */
+ else
+ type = 'u'; /* unicast */
+ break;
+
+ case MODE_ACTIVE:
+ type = 's'; /* symmetric active */
+ break; /* configured */
+
+ case MODE_PASSIVE:
+ type = 'S'; /* symmetric passive */
+ break; /* ephemeral */
+ }
+
+ /*
+ * Got everything, format the line
+ */
+ poll_sec = 1 << min(ppoll, hpoll);
+ if (pktversion > NTP_OLDVERSION)
+ c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
+ else
+ c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
+ if (numhosts > 1) {
+ if ((pvl == peervarlist || pvl == apeervarlist)
+ && have_dstadr) {
+ serverlocal = nntohost_col(&dstadr,
+ (size_t)min(LIB_BUFLENGTH - 1, maxhostlen),
+ TRUE);
+ } else {
+ if (currenthostisnum)
+ serverlocal = trunc_left(currenthost,
+ maxhostlen);
+ else
+ serverlocal = currenthost;
+ }
+ xprintf(fp, "%-*s ", (int)maxhostlen, serverlocal);
+ }
+ if (AF_UNSPEC == af || AF(&srcadr) == af) {
+ if (!have_srchost)
+ strlcpy(clock_name, nntohost(&srcadr),
+ sizeof(clock_name));
+ /* wide and long source - space over on next line */
+ /* allow for host + sp if > 1 and regular tally + source + sp */
+ if (wideremote && 15 < strlen(clock_name))
+ xprintf(fp, "%c%s\n%*s", c, clock_name,
+ ((numhosts > 1) ? (int)maxhostlen + 1 : 0)
+ + 1 + 15 + 1, "");
+ else
+ xprintf(fp, "%c%-15.15s ", c, clock_name);
+ if ((flash & TEST12) && (pvl != opeervarlist)) {
+ drlen = xprintf(fp, "(loop)");
+ } else if (!have_da_rid) {
+ drlen = 0;
+ } else {
+ drlen = strlen(dstadr_refid);
+ makeascii(drlen, dstadr_refid, fp);
+ }
+ if (pvl == apeervarlist) {
+ while (drlen++ < 9)
+ xputc(' ', fp);
+ xprintf(fp, "%-6d", associd);
+ } else {
+ while (drlen++ < 15)
+ xputc(' ', fp);
+ }
+ xprintf(fp,
+ " %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n",
+ stratum, type,
+ prettyinterval(whenbuf, sizeof(whenbuf),
+ when(&ts, &rec, &reftime)),
+ prettyinterval(pollbuf, sizeof(pollbuf),
+ (int)poll_sec),
+ reach, ulfptoms(&estdelay, 3),
+ lfptoms(&estoffset, 3),
+ (have_jitter)
+ ? ulfptoms(&estjitter, 3)
+ : ulfptoms(&estdisp, 3));
+ return (1);
+ }
+ else
+ return(1);
+}
+
+
+/*
+ * dogetpeers - given an association ID, read and print the spreadsheet
+ * peer variables.
+ */
+static int
+dogetpeers(
+ struct varlist *pvl,
+ associd_t associd,
+ FILE *fp,
+ int af
+ )
+{
+ const char *datap;
+ int res;
+ size_t dsize;
+ u_short rstatus;
+
+#ifdef notdef
+ res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
+ &dsize, &datap);
+#else
+ /*
+ * Damn fuzzballs
+ */
+ res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
+ &dsize, &datap);
+#endif
+
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ if (numhosts > 1)
+ xprintf(stderr, "server=%s ", currenthost);
+ xprintf(stderr,
+ "***No information returned for association %u\n",
+ associd);
+ return 0;
+ }
+
+ return doprintpeers(pvl, associd, (int)rstatus, dsize, datap,
+ fp, af);
+}
+
+
+/*
+ * peers - print a peer spreadsheet
+ */
+static void
+dopeers(
+ int showall,
+ FILE *fp,
+ int af
+ )
+{
+ u_int u;
+ char fullname[LENHOSTNAME];
+ sockaddr_u netnum;
+ const char * name_or_num;
+ size_t sl;
+
+ if (!dogetassoc(fp))
+ return;
+
+ for (u = 0; u < numhosts; u++) {
+ if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
+ name_or_num = nntohost(&netnum);
+ sl = strlen(name_or_num);
+ maxhostlen = max(maxhostlen, sl);
+ }
+ }
+ if (numhosts > 1)
+ xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
+ "server (local)");
+ xprintf(fp,
+ " remote refid st t when poll reach delay offset jitter\n");
+ if (numhosts > 1)
+ for (u = 0; u <= maxhostlen; u++)
+ xprintf(fp, "=");
+ xprintf(fp,
+ "==============================================================================\n");
+
+ for (u = 0; u < numassoc; u++) {
+ if (!showall &&
+ !(CTL_PEER_STATVAL(assoc_cache[u].status)
+ & (CTL_PST_CONFIG|CTL_PST_REACH))) {
+ if (debug)
+ xprintf(stderr, "eliding [%d]\n",
+ (int)assoc_cache[u].assid);
+ continue;
+ }
+ if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid,
+ fp, af))
+ return;
+ }
+ return;
+}
+
+
+/*
+ * doapeers - print a peer spreadsheet with assocIDs
+ */
+static void
+doapeers(
+ int showall,
+ FILE *fp,
+ int af
+ )
+{
+ u_int u;
+ char fullname[LENHOSTNAME];
+ sockaddr_u netnum;
+ const char * name_or_num;
+ size_t sl;
+
+ if (!dogetassoc(fp))
+ return;
+
+ for (u = 0; u < numhosts; u++) {
+ if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
+ name_or_num = nntohost(&netnum);
+ sl = strlen(name_or_num);
+ maxhostlen = max(maxhostlen, sl);
+ }
+ }
+ if (numhosts > 1)
+ xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
+ "server (local)");
+ xprintf(fp,
+ " remote refid assid st t when poll reach delay offset jitter\n");
+ if (numhosts > 1)
+ for (u = 0; u <= maxhostlen; u++)
+ xprintf(fp, "=");
+ xprintf(fp,
+ "==============================================================================\n");
+
+ for (u = 0; u < numassoc; u++) {
+ if (!showall &&
+ !(CTL_PEER_STATVAL(assoc_cache[u].status)
+ & (CTL_PST_CONFIG|CTL_PST_REACH))) {
+ if (debug)
+ xprintf(stderr, "eliding [%d]\n",
+ (int)assoc_cache[u].assid);
+ continue;
+ }
+ if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid,
+ fp, af))
+ return;
+ }
+ return;
+}
+
+
+/*
+ * peers - print a peer spreadsheet
+ */
+/*ARGSUSED*/
+static void
+peers(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (drefid == REFID_HASH) {
+ apeers(pcmd, fp);
+ } else {
+ int af = 0;
+
+ if (pcmd->nargs == 1) {
+ if (pcmd->argval->ival == 6)
+ af = AF_INET6;
+ else
+ af = AF_INET;
+ }
+ dopeers(0, fp, af);
+ }
+}
+
+
+/*
+ * apeers - print a peer spreadsheet, with assocIDs
+ */
+/*ARGSUSED*/
+static void
+apeers(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ int af = 0;
+
+ if (pcmd->nargs == 1) {
+ if (pcmd->argval->ival == 6)
+ af = AF_INET6;
+ else
+ af = AF_INET;
+ }
+ doapeers(0, fp, af);
+}
+
+
+/*
+ * lpeers - print a peer spreadsheet including all fuzzball peers
+ */
+/*ARGSUSED*/
+static void
+lpeers(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ int af = 0;
+
+ if (pcmd->nargs == 1) {
+ if (pcmd->argval->ival == 6)
+ af = AF_INET6;
+ else
+ af = AF_INET;
+ }
+ dopeers(1, fp, af);
+}
+
+
+/*
+ * opeers - print a peer spreadsheet
+ */
+static void
+doopeers(
+ int showall,
+ FILE *fp,
+ int af
+ )
+{
+ u_int i;
+ char fullname[LENHOSTNAME];
+ sockaddr_u netnum;
+
+ if (!dogetassoc(fp))
+ return;
+
+ for (i = 0; i < numhosts; ++i) {
+ if (getnetnum(chosts[i].name, &netnum, fullname, af))
+ if (strlen(fullname) > maxhostlen)
+ maxhostlen = strlen(fullname);
+ }
+ if (numhosts > 1)
+ xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
+ "server");
+ xprintf(fp,
+ " remote local st t when poll reach delay offset disp\n");
+ if (numhosts > 1)
+ for (i = 0; i <= maxhostlen; ++i)
+ xprintf(fp, "=");
+ xprintf(fp,
+ "==============================================================================\n");
+
+ for (i = 0; i < numassoc; i++) {
+ if (!showall &&
+ !(CTL_PEER_STATVAL(assoc_cache[i].status) &
+ (CTL_PST_CONFIG | CTL_PST_REACH)))
+ continue;
+ if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af))
+ return;
+ }
+ return;
+}
+
+
+/*
+ * opeers - print a peer spreadsheet the old way
+ */
+/*ARGSUSED*/
+static void
+opeers(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ int af = 0;
+
+ if (pcmd->nargs == 1) {
+ if (pcmd->argval->ival == 6)
+ af = AF_INET6;
+ else
+ af = AF_INET;
+ }
+ doopeers(0, fp, af);
+}
+
+
+/*
+ * lopeers - print a peer spreadsheet including all fuzzball peers
+ */
+/*ARGSUSED*/
+static void
+lopeers(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ int af = 0;
+
+ if (pcmd->nargs == 1) {
+ if (pcmd->argval->ival == 6)
+ af = AF_INET6;
+ else
+ af = AF_INET;
+ }
+ doopeers(1, fp, af);
+}
+
+
+/*
+ * config - send a configuration command to a remote host
+ */
+static void
+config (
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ const char *cfgcmd;
+ u_short rstatus;
+ size_t rsize;
+ const char *rdata;
+ char *resp;
+ int res;
+ int col;
+ int i;
+
+ cfgcmd = pcmd->argval[0].string;
+
+ if (debug > 2)
+ xprintf(stderr,
+ "In Config\n"
+ "Keyword = %s\n"
+ "Command = %s\n", pcmd->keyword, cfgcmd);
+
+ res = doquery(CTL_OP_CONFIGURE, 0, 1,
+ strlen(cfgcmd), cfgcmd,
+ &rstatus, &rsize, &rdata);
+
+ if (res != 0)
+ return;
+
+ if (rsize > 0 && '\n' == rdata[rsize - 1])
+ rsize--;
+
+ resp = emalloc(rsize + 1);
+ memcpy(resp, rdata, rsize);
+ resp[rsize] = '\0';
+
+ col = -1;
+ if (1 == sscanf(resp, "column %d syntax error", &col)
+ && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
+ if (interactive)
+ xputs(" *", stdout); /* "ntpq> :config " */
+ else
+ printf("%s\n", cfgcmd);
+ for (i = 0; i < col; i++)
+ xputc('_', stdout);
+ xputs("^\n", stdout);
+ }
+ printf("%s\n", resp);
+ free(resp);
+}
+
+
+/*
+ * config_from_file - remotely configure an ntpd daemon using the
+ * specified configuration file
+ * SK: This function is a kludge at best and is full of bad design
+ * bugs:
+ * 1. ntpq uses UDP, which means that there is no guarantee of in-order,
+ * error-free delivery.
+ * 2. The maximum length of a packet is constrained, and as a result, the
+ * maximum length of a line in a configuration file is constrained.
+ * Longer lines will lead to unpredictable results.
+ * 3. Since this function is sending a line at a time, we can't update
+ * the control key through the configuration file (YUCK!!)
+ *
+ * Pearly: There are a few places where 'size_t' is cast to 'int' based
+ * on the assumption that 'int' can hold the size of the involved
+ * buffers without overflow.
+ */
+static void
+config_from_file (
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ u_short rstatus;
+ size_t rsize;
+ const char *rdata;
+ char * cp;
+ int res;
+ FILE *config_fd;
+ char config_cmd[MAXLINE];
+ size_t config_len;
+ int i;
+ int retry_limit;
+
+ if (debug > 2)
+ xprintf(stderr,
+ "In Config\n"
+ "Keyword = %s\n"
+ "Filename = %s\n", pcmd->keyword,
+ pcmd->argval[0].string);
+
+ config_fd = fopen(pcmd->argval[0].string, "r");
+ if (NULL == config_fd) {
+ printf("ERROR!! Couldn't open file: %s\n",
+ pcmd->argval[0].string);
+ return;
+ }
+
+ printf("Sending configuration file, one line at a time.\n");
+ i = 0;
+ while (fgets(config_cmd, MAXLINE, config_fd) != NULL) {
+ /* Eliminate comments first. */
+ cp = strchr(config_cmd, '#');
+ config_len = (NULL != cp)
+ ? (size_t)(cp - config_cmd)
+ : strlen(config_cmd);
+
+ /* [Bug 3015] make sure there's no trailing whitespace;
+ * the fix for [Bug 2853] on the server side forbids
+ * those. And don't transmit empty lines, as this would
+ * just be waste.
+ */
+ while (config_len != 0 &&
+ (u_char)config_cmd[config_len-1] <= ' ')
+ --config_len;
+ config_cmd[config_len] = '\0';
+
+ ++i;
+ if (0 == config_len)
+ continue;
+
+ retry_limit = 2;
+ do
+ res = doquery(CTL_OP_CONFIGURE, 0, 1,
+ config_len, config_cmd,
+ &rstatus, &rsize, &rdata);
+ while (res != 0 && retry_limit--);
+ if (res != 0) {
+ printf("Line No: %d query failed: %.*s\n"
+ "Subsequent lines not sent.\n",
+ i, (int)config_len, config_cmd);
+ fclose(config_fd);
+ return;
+ }
+
+ /* Right-strip the result code string, then output the
+ * last line executed, with result code. */
+ while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ')
+ --rsize;
+ printf("Line No: %d %.*s: %.*s\n", i,
+ (int)rsize, rdata,
+ (int)config_len, config_cmd);
+ }
+ printf("Done sending file\n");
+ fclose(config_fd);
+}
+
+
+static int
+fetch_nonce(
+ char * nonce,
+ size_t cb_nonce
+ )
+{
+ const char nonce_eq[] = "nonce=";
+ int qres;
+ u_short rstatus;
+ size_t rsize;
+ const char * rdata;
+ size_t chars;
+
+ /*
+ * Retrieve a nonce specific to this client to demonstrate to
+ * ntpd that we're capable of receiving responses to our source
+ * IP address, and thereby unlikely to be forging the source.
+ */
+ qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus,
+ &rsize, &rdata);
+ if (qres) {
+ xprintf(stderr, "nonce request failed\n");
+ return FALSE;
+ }
+
+ if ((size_t)rsize <= sizeof(nonce_eq) - 1 ||
+ strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) {
+ xprintf(stderr, "unexpected nonce response format: %.*s\n",
+ (int)rsize, rdata); /* cast is wobbly */
+ return FALSE;
+ }
+ chars = rsize - (sizeof(nonce_eq) - 1);
+ if (chars >= cb_nonce)
+ return FALSE;
+ memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars);
+ nonce[chars] = '\0';
+ while (chars > 0 &&
+ ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) {
+ chars--;
+ nonce[chars] = '\0';
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * add_mru Add and entry to mru list, hash table, and allocate
+ * and return a replacement.
+ * This is a helper for collect_mru_list().
+ */
+static mru *
+add_mru(
+ mru *add
+ )
+{
+ u_short hash;
+ mru *mon;
+ mru *unlinked;
+
+
+ hash = NTP_HASH_ADDR(&add->addr);
+ /* see if we have it among previously received entries */
+ for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
+ if (SOCK_EQ(&mon->addr, &add->addr))
+ break;
+ if (mon != NULL) {
+ if (!L_ISGEQ(&add->first, &mon->first)) {
+ xprintf(stderr,
+ "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n",
+ sptoa(&add->addr), add->last.l_ui,
+ add->last.l_uf, mon->last.l_ui,
+ mon->last.l_uf);
+ exit(1);
+ }
+ UNLINK_DLIST(mon, mlink);
+ UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
+ INSIST(unlinked == mon);
+ mru_dupes++;
+ TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui,
+ mon->last.l_uf));
+ }
+ LINK_DLIST(mru_list, add, mlink);
+ LINK_SLIST(hash_table[hash], add, hlink);
+ TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
+ add->last.l_ui, add->last.l_uf, add->count,
+ (int)add->mode, (int)add->ver, (u_int)add->rs,
+ add->first.l_ui, add->first.l_uf, sptoa(&add->addr)));
+ /* if we didn't update an existing entry, alloc replacement */
+ if (NULL == mon) {
+ mon = emalloc(sizeof(*mon));
+ mru_count++;
+ }
+ ZERO(*mon);
+
+ return mon;
+}
+
+
+/* MGOT macro is specific to collect_mru_list() */
+#define MGOT(bit) \
+ do { \
+ got |= (bit); \
+ if (MRU_GOT_ALL == got) { \
+ got = 0; \
+ mon = add_mru(mon); \
+ ci++; \
+ } \
+ } while (0)
+
+
+int
+mrulist_ctrl_c_hook(void)
+{
+ mrulist_interrupted = TRUE;
+ return TRUE;
+}
+
+
+static int
+collect_mru_list(
+ const char * parms,
+ l_fp * pnow
+ )
+{
+ const u_int sleep_msecs = 5;
+ static int ntpd_row_limit = MRU_ROW_LIMIT;
+ int c_mru_l_rc; /* this function's return code */
+ u_char got; /* MRU_GOT_* bits */
+ time_t next_report;
+ size_t cb;
+ mru *mon;
+ mru *head;
+ mru *recent;
+ int list_complete;
+ char nonce[128];
+ char buf[128];
+ char req_buf[CTL_MAX_DATA_LEN];
+ char *req;
+ char *req_end;
+ size_t chars;
+ int qres;
+ u_short rstatus;
+ size_t rsize;
+ const char *rdata;
+ int limit;
+ int frags;
+ int cap_frags;
+ char *tag;
+ char *val;
+ int si; /* server index in response */
+ int ci; /* client (our) index for validation */
+ int ri; /* request index (.# suffix) */
+ int mv;
+ l_fp newest;
+ l_fp last_older;
+ sockaddr_u addr_older;
+ int have_now;
+ int have_addr_older;
+ int have_last_older;
+ u_int restarted_count;
+ u_int nonce_uses;
+ u_short hash;
+ mru *unlinked;
+
+ if (!fetch_nonce(nonce, sizeof(nonce)))
+ return FALSE;
+
+ nonce_uses = 0;
+ restarted_count = 0;
+ mru_count = 0;
+ INIT_DLIST(mru_list, mlink);
+ cb = NTP_HASH_SIZE * sizeof(*hash_table);
+ INSIST(NULL == hash_table);
+ hash_table = emalloc_zero(cb);
+
+ c_mru_l_rc = FALSE;
+ list_complete = FALSE;
+ have_now = FALSE;
+ cap_frags = TRUE;
+ got = 0;
+ ri = 0;
+ cb = sizeof(*mon);
+ mon = emalloc_zero(cb);
+ ZERO(*pnow);
+ ZERO(last_older);
+ next_report = time(NULL) + MRU_REPORT_SECS;
+
+ limit = min(3 * MAXFRAGS, ntpd_row_limit);
+ frags = MAXFRAGS;
+ snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s",
+ nonce, frags, parms);
+ nonce_uses++;
+
+ while (TRUE) {
+ if (debug)
+ xprintf(stderr, "READ_MRU parms: %s\n", req_buf);
+
+ qres = doqueryex(CTL_OP_READ_MRU, 0, 0,
+ strlen(req_buf), req_buf,
+ &rstatus, &rsize, &rdata, TRUE);
+
+ if (CERR_UNKNOWNVAR == qres && ri > 0) {
+ /*
+ * None of the supplied prior entries match, so
+ * toss them from our list and try again.
+ */
+ if (debug)
+ xprintf(stderr,
+ "no overlap between %d prior entries and server MRU list\n",
+ ri);
+ while (ri--) {
+ recent = HEAD_DLIST(mru_list, mlink);
+ INSIST(recent != NULL);
+ if (debug)
+ xprintf(stderr,
+ "tossing prior entry %s to resync\n",
+ sptoa(&recent->addr));
+ UNLINK_DLIST(recent, mlink);
+ hash = NTP_HASH_ADDR(&recent->addr);
+ UNLINK_SLIST(unlinked, hash_table[hash],
+ recent, hlink, mru);
+ INSIST(unlinked == recent);
+ free(recent);
+ mru_count--;
+ }
+ if (NULL == HEAD_DLIST(mru_list, mlink)) {
+ restarted_count++;
+ if (restarted_count > 8) {
+ xprintf(stderr,
+ "Giving up after 8 restarts from the beginning.\n"
+ "With high-traffic NTP servers, this can occur if the\n"
+ "MRU list is limited to less than about 16 seconds' of\n"
+ "entries. See the 'mru' ntp.conf directive to adjust.\n");
+ goto cleanup_return;
+ }
+ if (debug)
+ xprintf(stderr,
+ "---> Restarting from the beginning, retry #%u\n",
+ restarted_count);
+ }
+ } else if (CERR_UNKNOWNVAR == qres) {
+ xprintf(stderr,
+ "CERR_UNKNOWNVAR from ntpd but no priors given.\n");
+ goto cleanup_return;
+ } else if (CERR_BADVALUE == qres) {
+ if (cap_frags) {
+ cap_frags = FALSE;
+ if (debug)
+ xprintf(stderr,
+ "Reverted to row limit from fragments limit.\n");
+ } else {
+ /* ntpd has lower cap on row limit */
+ ntpd_row_limit--;
+ limit = min(limit, ntpd_row_limit);
+ if (debug)
+ xprintf(stderr,
+ "Row limit reduced to %d following CERR_BADVALUE.\n",
+ limit);
+ }
+ } else if (ERR_INCOMPLETE == qres ||
+ ERR_TIMEOUT == qres) {
+ /*
+ * Reduce the number of rows/frags requested by
+ * half to recover from lost response fragments.
+ */
+ if (cap_frags) {
+ frags = max(2, frags / 2);
+ if (debug)
+ xprintf(stderr,
+ "Frag limit reduced to %d following incomplete response.\n",
+ frags);
+ } else {
+ limit = max(2, limit / 2);
+ if (debug)
+ xprintf(stderr,
+ "Row limit reduced to %d following incomplete response.\n",
+ limit);
+ }
+ } else if (qres) {
+ show_error_msg(qres, 0);
+ goto cleanup_return;
+ }
+ /*
+ * This is a cheap cop-out implementation of rawmode
+ * output for mrulist. A better approach would be to
+ * dump similar output after the list is collected by
+ * ntpq with a continuous sequence of indexes. This
+ * cheap approach has indexes resetting to zero for
+ * each query/response, and duplicates are not
+ * coalesced.
+ */
+ if (!qres && rawmode)
+ printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
+ ci = 0;
+ have_addr_older = FALSE;
+ have_last_older = FALSE;
+ while (!qres && nextvar(&rsize, &rdata, &tag, &val)) {
+ INSIST(tag && val);
+ if (debug > 1)
+ xprintf(stderr, "nextvar gave: %s = %s\n",
+ tag, val);
+ switch(tag[0]) {
+
+ case 'a':
+ if (!strcmp(tag, "addr.older")) {
+ if (!have_last_older) {
+ xprintf(stderr,
+ "addr.older %s before last.older\n",
+ val);
+ goto cleanup_return;
+ }
+ if (!decodenetnum(val, &addr_older)) {
+ xprintf(stderr,
+ "addr.older %s garbled\n",
+ val);
+ goto cleanup_return;
+ }
+ hash = NTP_HASH_ADDR(&addr_older);
+ for (recent = hash_table[hash];
+ recent != NULL;
+ recent = recent->hlink)
+ if (ADDR_PORT_EQ(
+ &addr_older,
+ &recent->addr))
+ break;
+ if (NULL == recent) {
+ xprintf(stderr,
+ "addr.older %s not in hash table\n",
+ val);
+ goto cleanup_return;
+ }
+ if (!L_ISEQU(&last_older,
+ &recent->last)) {
+ xprintf(stderr,
+ "last.older %08x.%08x mismatches %08x.%08x expected.\n",
+ last_older.l_ui,
+ last_older.l_uf,
+ recent->last.l_ui,
+ recent->last.l_uf);
+ goto cleanup_return;
+ }
+ have_addr_older = TRUE;
+ } else if (1 != sscanf(tag, "addr.%d", &si)
+ || si != ci)
+ goto nomatch;
+ else if (decodenetnum(val, &mon->addr))
+ MGOT(MRU_GOT_ADDR);
+ break;
+
+ case 'l':
+ if (!strcmp(tag, "last.older")) {
+ if ('0' != val[0] ||
+ 'x' != val[1] ||
+ !hextolfp(val + 2, &last_older)) {
+ xprintf(stderr,
+ "last.older %s garbled\n",
+ val);
+ goto cleanup_return;
+ }
+ have_last_older = TRUE;
+ } else if (!strcmp(tag, "last.newest")) {
+ if (0 != got) {
+ xprintf(stderr,
+ "last.newest %s before complete row, got = 0x%x\n",
+ val, (u_int)got);
+ goto cleanup_return;
+ }
+ if (!have_now) {
+ xprintf(stderr,
+ "last.newest %s before now=\n",
+ val);
+ goto cleanup_return;
+ }
+ head = HEAD_DLIST(mru_list, mlink);
+ if (NULL != head) {
+ if ('0' != val[0] ||
+ 'x' != val[1] ||
+ !hextolfp(val + 2, &newest) ||
+ !L_ISEQU(&newest,
+ &head->last)) {
+ xprintf(stderr,
+ "last.newest %s mismatches %08x.%08x",
+ val,
+ head->last.l_ui,
+ head->last.l_uf);
+ goto cleanup_return;
+ }
+ }
+ list_complete = TRUE;
+ } else if (1 != sscanf(tag, "last.%d", &si) ||
+ si != ci || '0' != val[0] ||
+ 'x' != val[1] ||
+ !hextolfp(val + 2, &mon->last)) {
+ goto nomatch;
+ } else {
+ MGOT(MRU_GOT_LAST);
+ /*
+ * allow interrupted retrieval,
+ * using most recent retrieved
+ * entry's last seen timestamp
+ * as the end of operation.
+ */
+ *pnow = mon->last;
+ }
+ break;
+
+ case 'f':
+ if (1 != sscanf(tag, "first.%d", &si) ||
+ si != ci || '0' != val[0] ||
+ 'x' != val[1] ||
+ !hextolfp(val + 2, &mon->first))
+ goto nomatch;
+ MGOT(MRU_GOT_FIRST);
+ break;
+
+ case 'n':
+ if (!strcmp(tag, "nonce")) {
+ strlcpy(nonce, val, sizeof(nonce));
+ nonce_uses = 0;
+ break; /* case */
+ } else if (strcmp(tag, "now") ||
+ '0' != val[0] ||
+ 'x' != val[1] ||
+ !hextolfp(val + 2, pnow))
+ goto nomatch;
+ have_now = TRUE;
+ break;
+
+ case 'c':
+ if (1 != sscanf(tag, "ct.%d", &si) ||
+ si != ci ||
+ 1 != sscanf(val, "%d", &mon->count)
+ || mon->count < 1)
+ goto nomatch;
+ MGOT(MRU_GOT_COUNT);
+ break;
+
+ case 'm':
+ if (1 != sscanf(tag, "mv.%d", &si) ||
+ si != ci ||
+ 1 != sscanf(val, "%d", &mv))
+ goto nomatch;
+ mon->mode = PKT_MODE(mv);
+ mon->ver = PKT_VERSION(mv);
+ MGOT(MRU_GOT_MV);
+ break;
+
+ case 'r':
+ if (1 != sscanf(tag, "rs.%d", &si) ||
+ si != ci ||
+ 1 != sscanf(val, "0x%hx", &mon->rs))
+ goto nomatch;
+ MGOT(MRU_GOT_RS);
+ break;
+
+ default:
+ nomatch:
+ /* empty stmt */ ;
+ /* ignore unknown tags */
+ }
+ }
+ if (have_now)
+ list_complete = TRUE;
+ if (list_complete) {
+ INSIST(0 == ri || have_addr_older);
+ }
+ if (mrulist_interrupted) {
+ printf("mrulist retrieval interrupted by operator.\n"
+ "Displaying partial client list.\n");
+ fflush(stdout);
+ }
+ if (list_complete || mrulist_interrupted) {
+ xprintf(stderr,
+ "\rRetrieved %u unique MRU entries and %u updates.\n",
+ mru_count, mru_dupes);
+ fflush(stderr);
+ break;
+ }
+ if (time(NULL) >= next_report) {
+ next_report += MRU_REPORT_SECS;
+ xprintf(stderr, "\r%u (%u updates) ", mru_count,
+ mru_dupes);
+ fflush(stderr);
+ }
+
+ /*
+ * Snooze for a bit between queries to let ntpd catch
+ * up with other duties.
+ */
+#ifdef SYS_WINNT
+ Sleep(sleep_msecs);
+#elif !defined(HAVE_NANOSLEEP)
+ sleep((sleep_msecs / 1000) + 1);
+#else
+ {
+ struct timespec interv = { 0,
+ 1000 * sleep_msecs };
+ nanosleep(&interv, NULL);
+ }
+#endif
+ /*
+ * If there were no errors, increase the number of rows
+ * to a maximum of 3 * MAXFRAGS (the most packets ntpq
+ * can handle in one response), on the assumption that
+ * no less than 3 rows fit in each packet, capped at
+ * our best guess at the server's row limit.
+ */
+ if (!qres) {
+ if (cap_frags) {
+ frags = min(MAXFRAGS, frags + 1);
+ } else {
+ limit = min3(3 * MAXFRAGS,
+ ntpd_row_limit,
+ max(limit + 1,
+ limit * 33 / 32));
+ }
+ }
+ /*
+ * prepare next query with as many address and last-seen
+ * timestamps as will fit in a single packet.
+ */
+ req = req_buf;
+ req_end = req_buf + sizeof(req_buf);
+#define REQ_ROOM (req_end - req)
+ snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce,
+ (cap_frags)
+ ? "frags"
+ : "limit",
+ (cap_frags)
+ ? frags
+ : limit,
+ parms);
+ req += strlen(req);
+ nonce_uses++;
+ if (nonce_uses >= 4) {
+ if (!fetch_nonce(nonce, sizeof(nonce)))
+ goto cleanup_return;
+ nonce_uses = 0;
+ }
+
+
+ for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);
+ recent != NULL;
+ ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) {
+
+ snprintf(buf, sizeof(buf),
+ ", addr.%d=%s, last.%d=0x%08x.%08x",
+ ri, sptoa(&recent->addr), ri,
+ recent->last.l_ui, recent->last.l_uf);
+ chars = strlen(buf);
+ if ((size_t)REQ_ROOM <= chars)
+ break;
+ memcpy(req, buf, chars + 1);
+ req += chars;
+ }
+ }
+
+ c_mru_l_rc = TRUE;
+ goto retain_hash_table;
+
+cleanup_return:
+ free(hash_table);
+ hash_table = NULL;
+
+retain_hash_table:
+ if (mon != NULL)
+ free(mon);
+
+ return c_mru_l_rc;
+}
+
+
+/*
+ * qcmp_mru_addr - sort MRU entries by remote address.
+ *
+ * All IPv4 addresses sort before any IPv6, addresses are sorted by
+ * value within address family.
+ */
+static int
+qcmp_mru_addr(
+ const void *v1,
+ const void *v2
+ )
+{
+ const mru * const * ppm1 = v1;
+ const mru * const * ppm2 = v2;
+ const mru * pm1;
+ const mru * pm2;
+ u_short af1;
+ u_short af2;
+ size_t cmplen;
+ size_t addr_off;
+
+ pm1 = *ppm1;
+ pm2 = *ppm2;
+
+ af1 = AF(&pm1->addr);
+ af2 = AF(&pm2->addr);
+
+ if (af1 != af2)
+ return (AF_INET == af1)
+ ? -1
+ : 1;
+
+ cmplen = SIZEOF_INADDR(af1);
+ addr_off = (AF_INET == af1)
+ ? offsetof(struct sockaddr_in, sin_addr)
+ : offsetof(struct sockaddr_in6, sin6_addr);
+
+ return memcmp((const char *)&pm1->addr + addr_off,
+ (const char *)&pm2->addr + addr_off,
+ cmplen);
+}
+
+
+static int
+qcmp_mru_r_addr(
+ const void *v1,
+ const void *v2
+ )
+{
+ return -qcmp_mru_addr(v1, v2);
+}
+
+
+/*
+ * qcmp_mru_count - sort MRU entries by times seen (hit count).
+ */
+static int
+qcmp_mru_count(
+ const void *v1,
+ const void *v2
+ )
+{
+ const mru * const * ppm1 = v1;
+ const mru * const * ppm2 = v2;
+ const mru * pm1;
+ const mru * pm2;
+
+ pm1 = *ppm1;
+ pm2 = *ppm2;
+
+ return (pm1->count < pm2->count)
+ ? -1
+ : ((pm1->count == pm2->count)
+ ? 0
+ : 1);
+}
+
+
+static int
+qcmp_mru_r_count(
+ const void *v1,
+ const void *v2
+ )
+{
+ return -qcmp_mru_count(v1, v2);
+}
+
+
+/*
+ * qcmp_mru_avgint - sort MRU entries by average interval.
+ */
+static int
+qcmp_mru_avgint(
+ const void *v1,
+ const void *v2
+ )
+{
+ const mru * const * ppm1 = v1;
+ const mru * const * ppm2 = v2;
+ const mru * pm1;
+ const mru * pm2;
+ l_fp interval;
+ double avg1;
+ double avg2;
+
+ pm1 = *ppm1;
+ pm2 = *ppm2;
+
+ interval = pm1->last;
+ L_SUB(&interval, &pm1->first);
+ LFPTOD(&interval, avg1);
+ avg1 /= pm1->count;
+
+ interval = pm2->last;
+ L_SUB(&interval, &pm2->first);
+ LFPTOD(&interval, avg2);
+ avg2 /= pm2->count;
+
+ if (avg1 < avg2)
+ return -1;
+ else if (avg1 > avg2)
+ return 1;
+
+ /* secondary sort on lstint - rarely tested */
+ if (L_ISEQU(&pm1->last, &pm2->last))
+ return 0;
+ else if (L_ISGEQ(&pm1->last, &pm2->last))
+ return -1;
+ else
+ return 1;
+}
+
+
+static int
+qcmp_mru_r_avgint(
+ const void *v1,
+ const void *v2
+ )
+{
+ return -qcmp_mru_avgint(v1, v2);
+}
+
+
+/*
+ * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most
+ * Recently Used (seen) remote address list from ntpd.
+ *
+ * Similar to ntpdc's monlist command, but not limited to a single
+ * request/response, and thereby not limited to a few hundred remote
+ * addresses.
+ *
+ * See ntpd/ntp_control.c read_mru_list() for comments on the way
+ * CTL_OP_READ_MRU is designed to be used.
+ *
+ * mrulist intentionally differs from monlist in the way the avgint
+ * column is calculated. monlist includes the time after the last
+ * packet from the client until the monlist query time in the average,
+ * while mrulist excludes it. That is, monlist's average interval grows
+ * over time for remote addresses not heard from in some time, while it
+ * remains unchanged in mrulist. This also affects the avgint value for
+ * entries representing a single packet, with identical first and last
+ * timestamps. mrulist shows 0 avgint, monlist shows a value identical
+ * to lstint.
+ */
+static void
+mrulist(
+ struct parse * pcmd,
+ FILE * fp
+ )
+{
+ const char mincount_eq[] = "mincount=";
+ const char resall_eq[] = "resall=";
+ const char resany_eq[] = "resany=";
+ const char maxlstint_eq[] = "maxlstint=";
+ const char laddr_eq[] = "laddr=";
+ const char sort_eq[] = "sort=";
+ mru_sort_order order;
+ size_t n;
+ char parms_buf[128];
+ char buf[24];
+ char *parms;
+ const char *arg;
+ size_t cb;
+ mru **sorted;
+ mru **ppentry;
+ mru *recent;
+ l_fp now;
+ l_fp interval;
+ double favgint;
+ double flstint;
+ int avgint;
+ int lstint;
+ size_t i;
+
+ mrulist_interrupted = FALSE;
+ push_ctrl_c_handler(&mrulist_ctrl_c_hook);
+ xprintf(stderr,
+ "Ctrl-C will stop MRU retrieval and display partial results.\n");
+ fflush(stderr);
+
+ order = MRUSORT_DEF;
+ parms_buf[0] = '\0';
+ parms = parms_buf;
+ for (i = 0; i < pcmd->nargs; i++) {
+ arg = pcmd->argval[i].string;
+ if (arg != NULL) {
+ cb = strlen(arg) + 1;
+ if ((!strncmp(resall_eq, arg, sizeof(resall_eq)
+ - 1) || !strncmp(resany_eq, arg,
+ sizeof(resany_eq) - 1) || !strncmp(
+ mincount_eq, arg, sizeof(mincount_eq) - 1)
+ || !strncmp(laddr_eq, arg, sizeof(laddr_eq)
+ - 1) || !strncmp(maxlstint_eq, arg,
+ sizeof(laddr_eq) - 1)) && parms + cb + 2 <=
+ parms_buf + sizeof(parms_buf)) {
+ /* these are passed intact to ntpd */
+ memcpy(parms, ", ", 2);
+ parms += 2;
+ memcpy(parms, arg, cb);
+ parms += cb - 1;
+ } else if (!strncmp(sort_eq, arg,
+ sizeof(sort_eq) - 1)) {
+ arg += sizeof(sort_eq) - 1;
+ for (n = 0;
+ n < COUNTOF(mru_sort_keywords);
+ n++)
+ if (!strcmp(mru_sort_keywords[n],
+ arg))
+ break;
+ if (n < COUNTOF(mru_sort_keywords))
+ order = n;
+ } else if (!strcmp("limited", arg) ||
+ !strcmp("kod", arg)) {
+ /* transform to resany=... */
+ snprintf(buf, sizeof(buf),
+ ", resany=0x%x",
+ ('k' == arg[0])
+ ? RES_KOD
+ : RES_LIMITED);
+ cb = 1 + strlen(buf);
+ if (parms + cb <
+ parms_buf + sizeof(parms_buf)) {
+ memcpy(parms, buf, cb);
+ parms += cb - 1;
+ }
+ } else
+ xprintf(stderr,
+ "ignoring unrecognized mrulist parameter: %s\n",
+ arg);
+ }
+ }
+ parms = parms_buf;
+
+ if (!collect_mru_list(parms, &now))
+ return;
+
+ /* display the results */
+ if (rawmode)
+ goto cleanup_return;
+
+ /* construct an array of entry pointers in default order */
+ sorted = eallocarray(mru_count, sizeof(*sorted));
+ ppentry = sorted;
+ if (MRUSORT_R_DEF != order) {
+ ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
+ INSIST(ppentry < sorted + mru_count);
+ *ppentry = recent;
+ ppentry++;
+ ITER_DLIST_END()
+ } else {
+ REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
+ INSIST(ppentry < sorted + mru_count);
+ *ppentry = recent;
+ ppentry++;
+ REV_ITER_DLIST_END()
+ }
+
+ if (ppentry - sorted != (int)mru_count) {
+ xprintf(stderr,
+ "mru_count %u should match MRU list depth %ld.\n",
+ mru_count, (long)(ppentry - sorted));
+ free(sorted);
+ goto cleanup_return;
+ }
+
+ /* re-sort sorted[] if not default or reverse default */
+ if (MRUSORT_R_DEF < order)
+ qsort(sorted, mru_count, sizeof(sorted[0]),
+ mru_qcmp_table[order]);
+
+ mrulist_interrupted = FALSE;
+ printf( "lstint avgint rstr r m v count rport remote address\n"
+ "==============================================================================\n");
+ /* '=' x 78 */
+ for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) {
+ recent = *ppentry;
+ interval = now;
+ L_SUB(&interval, &recent->last);
+ LFPTOD(&interval, flstint);
+ lstint = (int)(flstint + 0.5);
+ interval = recent->last;
+ L_SUB(&interval, &recent->first);
+ LFPTOD(&interval, favgint);
+ favgint /= recent->count;
+ avgint = (int)(favgint + 0.5);
+ xprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n",
+ lstint, avgint, recent->rs,
+ (RES_KOD & recent->rs)
+ ? 'K'
+ : (RES_LIMITED & recent->rs)
+ ? 'L'
+ : '.',
+ (int)recent->mode, (int)recent->ver,
+ recent->count, SRCPORT(&recent->addr),
+ nntohost(&recent->addr));
+ if (showhostnames)
+ fflush(fp);
+ if (mrulist_interrupted) {
+ xputs("\n --interrupted--\n", fp);
+ fflush(fp);
+ break;
+ }
+ }
+ fflush(fp);
+ if (debug) {
+ xprintf(stderr,
+ "--- completed, freeing sorted[] pointers\n");
+ fflush(stderr);
+ }
+ free(sorted);
+
+cleanup_return:
+ if (debug) {
+ xprintf(stderr, "... freeing MRU entries\n");
+ fflush(stderr);
+ }
+ ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
+ free(recent);
+ ITER_DLIST_END()
+ if (debug) {
+ xprintf(stderr, "... freeing hash_table[]\n");
+ fflush(stderr);
+ }
+ free(hash_table);
+ hash_table = NULL;
+ INIT_DLIST(mru_list, mlink);
+
+ pop_ctrl_c_handler(&mrulist_ctrl_c_hook);
+}
+
+
+/*
+ * validate_ifnum - helper for ifstats()
+ *
+ * Ensures rows are received in order and complete.
+ */
+static void
+validate_ifnum(
+ FILE * fp,
+ u_int ifnum,
+ int * pfields,
+ ifstats_row * prow
+ )
+{
+ if (prow->ifnum == ifnum)
+ return;
+ if (prow->ifnum + 1 <= ifnum) {
+ if (*pfields < IFSTATS_FIELDS)
+ xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
+ *pfields, IFSTATS_FIELDS);
+ *pfields = 0;
+ prow->ifnum = ifnum;
+ return;
+ }
+ xprintf(stderr,
+ "received if index %u, have %d of %d fields for index %u, aborting.\n",
+ ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum);
+ exit(1);
+}
+
+
+/*
+ * another_ifstats_field - helper for ifstats()
+ *
+ * If all fields for the row have been received, print it.
+ */
+static void
+another_ifstats_field(
+ int * pfields,
+ ifstats_row * prow,
+ FILE * fp
+ )
+{
+ u_int ifnum;
+
+ (*pfields)++;
+ /* we understand 12 tags */
+ if (IFSTATS_FIELDS > *pfields)
+ return;
+ /*
+ " interface name send\n"
+ " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
+ "==============================================================================\n");
+ */
+ xprintf(fp,
+ "%3u %-24.24s %c %4x %3u %2u %6u %6u %6u %5u %8d\n"
+ " %s\n",
+ prow->ifnum, prow->name,
+ (prow->enabled)
+ ? '.'
+ : 'D',
+ prow->flags, prow->ttl, prow->mcast_count,
+ prow->received, prow->sent, prow->send_errors,
+ prow->peer_count, prow->uptime, sptoa(&prow->addr));
+ if (!SOCK_UNSPEC(&prow->bcast))
+ xprintf(fp, " %s\n", sptoa(&prow->bcast));
+ ifnum = prow->ifnum;
+ ZERO(*prow);
+ prow->ifnum = ifnum;
+}
+
+
+/*
+ * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats.
+ */
+static void
+ifstats(
+ struct parse * pcmd,
+ FILE * fp
+ )
+{
+ const char addr_fmt[] = "addr.%u";
+ const char bcast_fmt[] = "bcast.%u";
+ const char en_fmt[] = "en.%u"; /* enabled */
+ const char flags_fmt[] = "flags.%u";
+ const char mc_fmt[] = "mc.%u"; /* mcast count */
+ const char name_fmt[] = "name.%u";
+ const char pc_fmt[] = "pc.%u"; /* peer count */
+ const char rx_fmt[] = "rx.%u";
+ const char tl_fmt[] = "tl.%u"; /* ttl */
+ const char tx_fmt[] = "tx.%u";
+ const char txerr_fmt[] = "txerr.%u";
+ const char up_fmt[] = "up.%u"; /* uptime */
+ const char * datap;
+ int qres;
+ size_t dsize;
+ u_short rstatus;
+ char * tag;
+ char * val;
+ int fields;
+ u_int ui;
+ ifstats_row row;
+ int comprende;
+ size_t len;
+
+ qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus,
+ &dsize, &datap);
+ if (qres) /* message already displayed */
+ return;
+
+ xprintf(fp,
+ " interface name send\n"
+ " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
+ "==============================================================================\n");
+ /* '=' x 78 */
+
+ ZERO(row);
+ fields = 0;
+ ui = 0;
+ while (nextvar(&dsize, &datap, &tag, &val)) {
+ INSIST(tag && val);
+ if (debug > 1)
+ xprintf(stderr, "nextvar gave: %s = %s\n", tag, val);
+ comprende = FALSE;
+ switch(tag[0]) {
+
+ case 'a':
+ if (1 == sscanf(tag, addr_fmt, &ui) &&
+ decodenetnum(val, &row.addr))
+ comprende = TRUE;
+ break;
+
+ case 'b':
+ if (1 == sscanf(tag, bcast_fmt, &ui) &&
+ ('\0' == *val ||
+ decodenetnum(val, &row.bcast)))
+ comprende = TRUE;
+ break;
+
+ case 'e':
+ if (1 == sscanf(tag, en_fmt, &ui) &&
+ 1 == sscanf(val, "%d", &row.enabled))
+ comprende = TRUE;
+ break;
+
+ case 'f':
+ if (1 == sscanf(tag, flags_fmt, &ui) &&
+ 1 == sscanf(val, "0x%x", &row.flags))
+ comprende = TRUE;
+ break;
+
+ case 'm':
+ if (1 == sscanf(tag, mc_fmt, &ui) &&
+ 1 == sscanf(val, "%u", &row.mcast_count))
+ comprende = TRUE;
+ break;
+
+ case 'n':
+ if (1 == sscanf(tag, name_fmt, &ui)) {
+ /* strip quotes */
+ len = strlen(val);
+ if (len >= 2 &&
+ len - 2 < sizeof(row.name)) {
+ len -= 2;
+ memcpy(row.name, val + 1, len);
+ row.name[len] = '\0';
+ comprende = TRUE;
+ }
+ }
+ break;
+
+ case 'p':
+ if (1 == sscanf(tag, pc_fmt, &ui) &&
+ 1 == sscanf(val, "%u", &row.peer_count))
+ comprende = TRUE;
+ break;
+
+ case 'r':
+ if (1 == sscanf(tag, rx_fmt, &ui) &&
+ 1 == sscanf(val, "%u", &row.received))
+ comprende = TRUE;
+ break;
+
+ case 't':
+ if (1 == sscanf(tag, tl_fmt, &ui) &&
+ 1 == sscanf(val, "%u", &row.ttl))
+ comprende = TRUE;
+ else if (1 == sscanf(tag, tx_fmt, &ui) &&
+ 1 == sscanf(val, "%u", &row.sent))
+ comprende = TRUE;
+ else if (1 == sscanf(tag, txerr_fmt, &ui) &&
+ 1 == sscanf(val, "%u", &row.send_errors))
+ comprende = TRUE;
+ break;
+
+ case 'u':
+ if (1 == sscanf(tag, up_fmt, &ui) &&
+ 1 == sscanf(val, "%u", &row.uptime))
+ comprende = TRUE;
+ break;
+ }
+
+ if (comprende) {
+ /* error out if rows out of order */
+ validate_ifnum(fp, ui, &fields, &row);
+ /* if the row is complete, print it */
+ another_ifstats_field(&fields, &row, fp);
+ }
+ }
+ if (fields != IFSTATS_FIELDS)
+ xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
+ fields, IFSTATS_FIELDS);
+
+ fflush(fp);
+}
+
+
+/*
+ * validate_reslist_idx - helper for reslist()
+ *
+ * Ensures rows are received in order and complete.
+ */
+static void
+validate_reslist_idx(
+ FILE * fp,
+ u_int idx,
+ int * pfields,
+ reslist_row * prow
+ )
+{
+ if (prow->idx == idx)
+ return;
+ if (prow->idx + 1 == idx) {
+ if (*pfields < RESLIST_FIELDS)
+ xprintf(fp, "Warning: incomplete row with %d (of %d) fields",
+ *pfields, RESLIST_FIELDS);
+ *pfields = 0;
+ prow->idx = idx;
+ return;
+ }
+ xprintf(stderr,
+ "received reslist index %u, have %d of %d fields for index %u, aborting.\n",
+ idx, *pfields, RESLIST_FIELDS, prow->idx);
+ exit(1);
+}
+
+
+/*
+ * another_reslist_field - helper for reslist()
+ *
+ * If all fields for the row have been received, print it.
+ */
+static void
+another_reslist_field(
+ int * pfields,
+ reslist_row * prow,
+ FILE * fp
+ )
+{
+ char addrmaskstr[128];
+ int prefix; /* subnet mask as prefix bits count */
+ u_int idx;
+
+ (*pfields)++;
+ /* we understand 4 tags */
+ if (RESLIST_FIELDS > *pfields)
+ return;
+
+ prefix = sockaddr_masktoprefixlen(&prow->mask);
+ if (prefix >= 0)
+ snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d",
+ stoa(&prow->addr), prefix);
+ else
+ snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s",
+ stoa(&prow->addr), stoa(&prow->mask));
+
+ /*
+ " hits addr/prefix or addr mask\n"
+ " restrictions\n"
+ "==============================================================================\n");
+ */
+ xprintf(fp,
+ "%10lu %s\n"
+ " %s\n",
+ prow->hits, addrmaskstr, prow->flagstr);
+ idx = prow->idx;
+ ZERO(*prow);
+ prow->idx = idx;
+}
+
+
+/*
+ * reslist - ntpq -c reslist modeled on ntpdc -c reslist.
+ */
+static void
+reslist(
+ struct parse * pcmd,
+ FILE * fp
+ )
+{
+ const char addr_fmtu[] = "addr.%u";
+ const char mask_fmtu[] = "mask.%u";
+ const char hits_fmt[] = "hits.%u";
+ const char flags_fmt[] = "flags.%u";
+ const char qdata[] = "addr_restrictions";
+ const int qdata_chars = COUNTOF(qdata) - 1;
+ const char * datap;
+ int qres;
+ size_t dsize;
+ u_short rstatus;
+ char * tag;
+ char * val;
+ int fields;
+ u_int ui;
+ reslist_row row;
+ int comprende;
+ size_t len;
+
+ qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars,
+ qdata, &rstatus, &dsize, &datap);
+ if (qres) /* message already displayed */
+ return;
+
+ xprintf(fp,
+ " hits addr/prefix or addr mask\n"
+ " restrictions\n"
+ "==============================================================================\n");
+ /* '=' x 78 */
+
+ ZERO(row);
+ fields = 0;
+ ui = 0;
+ while (nextvar(&dsize, &datap, &tag, &val)) {
+ INSIST(tag && val);
+ if (debug > 1)
+ xprintf(stderr, "nextvar gave: %s = %s\n", tag, val);
+ comprende = FALSE;
+ switch(tag[0]) {
+
+ case 'a':
+ if (1 == sscanf(tag, addr_fmtu, &ui) &&
+ decodenetnum(val, &row.addr))
+ comprende = TRUE;
+ break;
+
+ case 'f':
+ if (1 == sscanf(tag, flags_fmt, &ui)) {
+ if (NULL == val) {
+ row.flagstr[0] = '\0';
+ comprende = TRUE;
+ } else if ((len = strlen(val)) < sizeof(row.flagstr)) {
+ memcpy(row.flagstr, val, len);
+ row.flagstr[len] = '\0';
+ comprende = TRUE;
+ } else {
+ /* no flags, and still !comprende */
+ row.flagstr[0] = '\0';
+ }
+ }
+ break;
+
+ case 'h':
+ if (1 == sscanf(tag, hits_fmt, &ui) &&
+ 1 == sscanf(val, "%lu", &row.hits))
+ comprende = TRUE;
+ break;
+
+ case 'm':
+ if (1 == sscanf(tag, mask_fmtu, &ui) &&
+ decodenetnum(val, &row.mask))
+ comprende = TRUE;
+ break;
+ }
+
+ if (comprende) {
+ /* error out if rows out of order */
+ validate_reslist_idx(fp, ui, &fields, &row);
+ /* if the row is complete, print it */
+ another_reslist_field(&fields, &row, fp);
+ }
+ }
+ if (fields != RESLIST_FIELDS)
+ xprintf(fp, "Warning: incomplete row with %d (of %d) fields",
+ fields, RESLIST_FIELDS);
+
+ fflush(fp);
+}
+
+
+/*
+ * collect_display_vdc
+ */
+static void
+collect_display_vdc(
+ associd_t as,
+ vdc * table,
+ int decodestatus,
+ FILE * fp
+ )
+{
+ static const char * const suf[2] = { "adr", "port" };
+ static const char * const leapbits[4] = { "00", "01",
+ "10", "11" };
+ struct varlist vl[MAXLIST];
+ char tagbuf[32];
+ vdc *pvdc;
+ u_short rstatus;
+ size_t rsize;
+ const char *rdata;
+ int qres;
+ char *tag;
+ char *val;
+ u_int n;
+ size_t len;
+ int match;
+ u_long ul;
+ int vtype;
+ sockaddr_u sau;
+
+ ZERO(vl);
+ for (pvdc = table; pvdc->tag != NULL; pvdc++) {
+ ZERO(pvdc->v);
+ if (NTP_ADD != pvdc->type) {
+ doaddvlist(vl, pvdc->tag);
+ } else {
+ for (n = 0; n < COUNTOF(suf); n++) {
+ snprintf(tagbuf, sizeof(tagbuf), "%s%s",
+ pvdc->tag, suf[n]);
+ doaddvlist(vl, tagbuf);
+ }
+ }
+ }
+ qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize,
+ &rdata);
+ doclearvlist(vl);
+ if (qres)
+ return; /* error msg already displayed */
+
+ /*
+ * iterate over the response variables filling vdc_table with
+ * the retrieved values.
+ */
+ while (nextvar(&rsize, &rdata, &tag, &val)) {
+ INSIST(tag && val);
+ n = 0;
+ for (pvdc = table; pvdc->tag != NULL; pvdc++) {
+ len = strlen(pvdc->tag);
+ if (strncmp(tag, pvdc->tag, len))
+ continue;
+ if (NTP_ADD != pvdc->type) {
+ if ('\0' != tag[len])
+ continue;
+ break;
+ }
+ match = FALSE;
+ for (n = 0; n < COUNTOF(suf); n++) {
+ if (strcmp(tag + len, suf[n]))
+ continue;
+ match = TRUE;
+ break;
+ }
+ if (match)
+ break;
+ }
+ if (NULL == pvdc->tag)
+ continue;
+ switch (pvdc->type) {
+
+ case NTP_STR:
+ /* strip surrounding double quotes */
+ if ('"' == val[0]) {
+ len = strlen(val);
+ if (len > 0 && '"' == val[len - 1]) {
+ val[len - 1] = '\0';
+ val++;
+ }
+ }
+ /* fallthru */
+ case NTP_REFID: /* fallthru */
+ case NTP_MODE: /* fallthru */
+ case NTP_2BIT:
+ pvdc->v.str = estrdup(val);
+ break;
+
+ case NTP_LFP:
+ decodets(val, &pvdc->v.lfp);
+ break;
+
+ case NTP_ADP:
+ if (!decodenetnum(val, &pvdc->v.sau))
+ xprintf(stderr, "malformed %s=%s\n",
+ pvdc->tag, val);
+ break;
+
+ case NTP_ADD:
+ if (0 == n) { /* adr */
+ if (!decodenetnum(val, &pvdc->v.sau))
+ xprintf(stderr,
+ "malformed %s=%s\n",
+ pvdc->tag, val);
+ } else { /* port */
+ if (atouint(val, &ul))
+ SET_PORT(&pvdc->v.sau,
+ (u_short)ul);
+ }
+ break;
+ }
+ }
+
+ /* and display */
+ if (decodestatus) {
+ vtype = (0 == as)
+ ? TYPE_SYS
+ : TYPE_PEER;
+ xprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus,
+ statustoa(vtype, rstatus));
+ }
+
+ for (pvdc = table; pvdc->tag != NULL; pvdc++) {
+ switch (pvdc->type) {
+
+ case NTP_STR:
+ if (pvdc->v.str != NULL) {
+ xprintf(fp, "%s %s\n", pvdc->display,
+ pvdc->v.str);
+ free(pvdc->v.str);
+ pvdc->v.str = NULL;
+ }
+ break;
+
+ case NTP_ADD: /* fallthru */
+ case NTP_ADP:
+ xprintf(fp, "%s %s\n", pvdc->display,
+ nntohostp(&pvdc->v.sau));
+ break;
+
+ case NTP_LFP:
+ xprintf(fp, "%s %s\n", pvdc->display,
+ prettydate(&pvdc->v.lfp));
+ break;
+
+ case NTP_MODE:
+ atouint(pvdc->v.str, &ul);
+ xprintf(fp, "%s %s\n", pvdc->display,
+ modetoa((int)ul));
+ free(pvdc->v.str);
+ pvdc->v.str = NULL;
+ break;
+
+ case NTP_2BIT:
+ atouint(pvdc->v.str, &ul);
+ xprintf(fp, "%s %s\n", pvdc->display,
+ leapbits[ul & 0x3]);
+ free(pvdc->v.str);
+ pvdc->v.str = NULL;
+ break;
+
+ case NTP_REFID:
+ if (!decodenetnum(pvdc->v.str, &sau)) {
+ fprintf(fp, "%s %s\n", pvdc->display, /* Text fmt */
+ pvdc->v.str);
+ } else if (drefid == REFID_IPV4) {
+ fprintf(fp, "%s %s\n", pvdc->display, /* IPv4 fmt */
+ stoa(&sau));
+ } else {
+ fprintf (fp, "%s 0x%08x\n", pvdc->display, /* Hex / hash */
+ ntohl(addr2refid(&sau)));
+ }
+ free(pvdc->v.str);
+ pvdc->v.str = NULL;
+ break;
+
+ default:
+ xprintf(stderr, "unexpected vdc type %d for %s\n",
+ pvdc->type, pvdc->tag);
+ break;
+ }
+ }
+}
+
+
+/*
+ * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats
+ */
+static void
+sysstats(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ static vdc sysstats_vdc[] = {
+ VDC_INIT("ss_uptime", "uptime: ", NTP_STR),
+ VDC_INIT("ss_reset", "sysstats reset: ", NTP_STR),
+ VDC_INIT("ss_received", "packets received: ", NTP_STR),
+ VDC_INIT("ss_thisver", "current version: ", NTP_STR),
+ VDC_INIT("ss_oldver", "older version: ", NTP_STR),
+ VDC_INIT("ss_badformat", "bad length or format: ", NTP_STR),
+ VDC_INIT("ss_badauth", "authentication failed:", NTP_STR),
+ VDC_INIT("ss_declined", "declined: ", NTP_STR),
+ VDC_INIT("ss_restricted", "restricted: ", NTP_STR),
+ VDC_INIT("ss_limited", "rate limited: ", NTP_STR),
+ VDC_INIT("ss_kodsent", "KoD responses: ", NTP_STR),
+ VDC_INIT("ss_processed", "processed for time: ", NTP_STR),
+#if 0
+ VDC_INIT("ss_lamport", "Lamport violations: ", NTP_STR),
+ VDC_INIT("ss_tsrounding", "bad timestamp rounding:", NTP_STR),
+#endif
+ VDC_INIT(NULL, NULL, 0)
+ };
+
+ collect_display_vdc(0, sysstats_vdc, FALSE, fp);
+}
+
+
+/*
+ * sysinfo - modeled on ntpdc's sysinfo
+ */
+static void
+sysinfo(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ static vdc sysinfo_vdc[] = {
+ VDC_INIT("peeradr", "system peer: ", NTP_ADP),
+ VDC_INIT("peermode", "system peer mode: ", NTP_MODE),
+ VDC_INIT("leap", "leap indicator: ", NTP_2BIT),
+ VDC_INIT("stratum", "stratum: ", NTP_STR),
+ VDC_INIT("precision", "log2 precision: ", NTP_STR),
+ VDC_INIT("rootdelay", "root delay: ", NTP_STR),
+ VDC_INIT("rootdisp", "root dispersion: ", NTP_STR),
+ VDC_INIT("refid", "reference ID: ", NTP_REFID),
+ VDC_INIT("reftime", "reference time: ", NTP_LFP),
+ VDC_INIT("sys_jitter", "system jitter: ", NTP_STR),
+ VDC_INIT("clk_jitter", "clock jitter: ", NTP_STR),
+ VDC_INIT("clk_wander", "clock wander: ", NTP_STR),
+ VDC_INIT("bcastdelay", "broadcast delay: ", NTP_STR),
+ VDC_INIT("authdelay", "symm. auth. delay:", NTP_STR),
+ VDC_INIT(NULL, NULL, 0)
+ };
+
+ collect_display_vdc(0, sysinfo_vdc, TRUE, fp);
+}
+
+
+/*
+ * kerninfo - modeled on ntpdc's kerninfo
+ */
+static void
+kerninfo(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ static vdc kerninfo_vdc[] = {
+ VDC_INIT("koffset", "pll offset: ", NTP_STR),
+ VDC_INIT("kfreq", "pll frequency: ", NTP_STR),
+ VDC_INIT("kmaxerr", "maximum error: ", NTP_STR),
+ VDC_INIT("kesterr", "estimated error: ", NTP_STR),
+ VDC_INIT("kstflags", "kernel status: ", NTP_STR),
+ VDC_INIT("ktimeconst", "pll time constant: ", NTP_STR),
+ VDC_INIT("kprecis", "precision: ", NTP_STR),
+ VDC_INIT("kfreqtol", "frequency tolerance: ", NTP_STR),
+ VDC_INIT("kppsfreq", "pps frequency: ", NTP_STR),
+ VDC_INIT("kppsstab", "pps stability: ", NTP_STR),
+ VDC_INIT("kppsjitter", "pps jitter: ", NTP_STR),
+ VDC_INIT("kppscalibdur", "calibration interval ", NTP_STR),
+ VDC_INIT("kppscalibs", "calibration cycles: ", NTP_STR),
+ VDC_INIT("kppsjitexc", "jitter exceeded: ", NTP_STR),
+ VDC_INIT("kppsstbexc", "stability exceeded: ", NTP_STR),
+ VDC_INIT("kppscaliberrs", "calibration errors: ", NTP_STR),
+ VDC_INIT(NULL, NULL, 0)
+ };
+
+ collect_display_vdc(0, kerninfo_vdc, TRUE, fp);
+}
+
+
+/*
+ * monstats - implements ntpq -c monstats
+ */
+static void
+monstats(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ static vdc monstats_vdc[] = {
+ VDC_INIT("mru_enabled", "enabled: ", NTP_STR),
+ VDC_INIT("mru_depth", "addresses: ", NTP_STR),
+ VDC_INIT("mru_deepest", "peak addresses: ", NTP_STR),
+ VDC_INIT("mru_maxdepth", "maximum addresses: ", NTP_STR),
+ VDC_INIT("mru_mindepth", "reclaim above count:", NTP_STR),
+ VDC_INIT("mru_maxage", "reclaim older than: ", NTP_STR),
+ VDC_INIT("mru_mem", "kilobytes: ", NTP_STR),
+ VDC_INIT("mru_maxmem", "maximum kilobytes: ", NTP_STR),
+ VDC_INIT(NULL, NULL, 0)
+ };
+
+ collect_display_vdc(0, monstats_vdc, FALSE, fp);
+}
+
+
+/*
+ * iostats - ntpq -c iostats - network input and output counters
+ */
+static void
+iostats(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ static vdc iostats_vdc[] = {
+ VDC_INIT("iostats_reset", "time since reset: ", NTP_STR),
+ VDC_INIT("total_rbuf", "receive buffers: ", NTP_STR),
+ VDC_INIT("free_rbuf", "free receive buffers: ", NTP_STR),
+ VDC_INIT("used_rbuf", "used receive buffers: ", NTP_STR),
+ VDC_INIT("rbuf_lowater", "low water refills: ", NTP_STR),
+ VDC_INIT("io_dropped", "dropped packets: ", NTP_STR),
+ VDC_INIT("io_ignored", "ignored packets: ", NTP_STR),
+ VDC_INIT("io_received", "received packets: ", NTP_STR),
+ VDC_INIT("io_sent", "packets sent: ", NTP_STR),
+ VDC_INIT("io_sendfailed", "packet send failures: ", NTP_STR),
+ VDC_INIT("io_wakeups", "input wakeups: ", NTP_STR),
+ VDC_INIT("io_goodwakeups", "useful input wakeups: ", NTP_STR),
+ VDC_INIT(NULL, NULL, 0)
+ };
+
+ collect_display_vdc(0, iostats_vdc, FALSE, fp);
+}
+
+
+/*
+ * timerstats - ntpq -c timerstats - interval timer counters
+ */
+static void
+timerstats(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ static vdc timerstats_vdc[] = {
+ VDC_INIT("timerstats_reset", "time since reset: ", NTP_STR),
+ VDC_INIT("timer_overruns", "timer overruns: ", NTP_STR),
+ VDC_INIT("timer_xmts", "calls to transmit: ", NTP_STR),
+ VDC_INIT(NULL, NULL, 0)
+ };
+
+ collect_display_vdc(0, timerstats_vdc, FALSE, fp);
+}
+
+
+/*
+ * authinfo - implements ntpq -c authinfo
+ */
+static void
+authinfo(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ static vdc authinfo_vdc[] = {
+ VDC_INIT("authreset", "time since reset:", NTP_STR),
+ VDC_INIT("authkeys", "stored keys: ", NTP_STR),
+ VDC_INIT("authfreek", "free keys: ", NTP_STR),
+ VDC_INIT("authklookups", "key lookups: ", NTP_STR),
+ VDC_INIT("authknotfound", "keys not found: ", NTP_STR),
+ VDC_INIT("authkuncached", "uncached keys: ", NTP_STR),
+ VDC_INIT("authkexpired", "expired keys: ", NTP_STR),
+ VDC_INIT("authencrypts", "encryptions: ", NTP_STR),
+ VDC_INIT("authdecrypts", "decryptions: ", NTP_STR),
+ VDC_INIT(NULL, NULL, 0)
+ };
+
+ collect_display_vdc(0, authinfo_vdc, FALSE, fp);
+}
+
+
+/*
+ * pstats - show statistics for a peer
+ */
+static void
+pstats(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ static vdc pstats_vdc[] = {
+ VDC_INIT("src", "remote host: ", NTP_ADD),
+ VDC_INIT("dst", "local address: ", NTP_ADD),
+ VDC_INIT("timerec", "time last received: ", NTP_STR),
+ VDC_INIT("timer", "time until next send:", NTP_STR),
+ VDC_INIT("timereach", "reachability change: ", NTP_STR),
+ VDC_INIT("sent", "packets sent: ", NTP_STR),
+ VDC_INIT("received", "packets received: ", NTP_STR),
+ VDC_INIT("badauth", "bad authentication: ", NTP_STR),
+ VDC_INIT("bogusorg", "bogus origin: ", NTP_STR),
+ VDC_INIT("oldpkt", "duplicate: ", NTP_STR),
+ VDC_INIT("seldisp", "bad dispersion: ", NTP_STR),
+ VDC_INIT("selbroken", "bad reference time: ", NTP_STR),
+ VDC_INIT("candidate", "candidate order: ", NTP_STR),
+ VDC_INIT(NULL, NULL, 0)
+ };
+ associd_t associd;
+
+ associd = checkassocid(pcmd->argval[0].uval);
+ if (0 == associd)
+ return;
+
+ collect_display_vdc(associd, pstats_vdc, TRUE, fp);
+}
diff --git a/bsd/freebsd/contrib/ntp/ntpq/ntpq.c b/bsd/freebsd/contrib/ntp/ntpq/ntpq.c
new file mode 100644
index 0000000..0382c0f
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/ntpq/ntpq.c
@@ -0,0 +1,4174 @@
+/*
+ * ntpq - query an NTP server using mode 6 commands
+ */
+#include <config.h>
+#include <ctype.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef SYS_WINNT
+# include <mswsock.h>
+#endif
+#include <isc/net.h>
+#include <isc/result.h>
+
+#include "ntpq.h"
+#include "ntp_assert.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+#include "ntp_select.h"
+#include "ntp_assert.h"
+#include "lib_strbuf.h"
+#include "ntp_lineedit.h"
+#include "ntp_debug.h"
+#ifdef OPENSSL
+# include "openssl/evp.h"
+# include "openssl/objects.h"
+# include "openssl/err.h"
+# ifdef SYS_WINNT
+# include "openssl/opensslv.h"
+# if !defined(HAVE_EVP_MD_DO_ALL_SORTED) && OPENSSL_VERSION_NUMBER > 0x10000000L
+# define HAVE_EVP_MD_DO_ALL_SORTED 1
+# endif
+# endif
+# include "libssl_compat.h"
+# ifdef HAVE_OPENSSL_CMAC_H
+# include <openssl/cmac.h>
+# define CMAC "AES128CMAC"
+# endif
+#endif
+#include <ssl_applink.c>
+
+#include "ntp_libopts.h"
+#include "safecast.h"
+
+#ifdef SYS_VXWORKS /* vxWorks needs mode flag -casey*/
+# define open(name, flags) open(name, flags, 0777)
+# define SERVER_PORT_NUM 123
+#endif
+
+/* we use COMMAND as an autogen keyword */
+#ifdef COMMAND
+# undef COMMAND
+#endif
+
+/*
+ * Because we potentially understand a lot of commands we will run
+ * interactive if connected to a terminal.
+ */
+int interactive = 0; /* set to 1 when we should prompt */
+const char *prompt = "ntpq> "; /* prompt to ask him about */
+
+/*
+ * use old readvars behavior? --old-rv processing in ntpq resets
+ * this value based on the presence or absence of --old-rv. It is
+ * initialized to 1 here to maintain backward compatibility with
+ * libntpq clients such as ntpsnmpd, which are free to reset it as
+ * desired.
+ */
+int old_rv = 1;
+
+/*
+ * How should we display the refid?
+ * REFID_HASH, REFID_IPV4
+ */
+te_Refid drefid = -1;
+
+/*
+ * for get_systime()
+ */
+s_char sys_precision; /* local clock precision (log2 s) */
+
+/*
+ * Keyid used for authenticated requests. Obtained on the fly.
+ */
+u_long info_auth_keyid = 0;
+
+static int info_auth_keytype = NID_md5; /* MD5 */
+static size_t info_auth_hashlen = 16; /* MD5 */
+u_long current_time; /* needed by authkeys; not used */
+
+/*
+ * Flag which indicates we should always send authenticated requests
+ */
+int always_auth = 0;
+
+/*
+ * Flag which indicates raw mode output.
+ */
+int rawmode = 0;
+
+/*
+ * Packet version number we use
+ */
+u_char pktversion = NTP_OLDVERSION + 1;
+
+
+/*
+ * Format values
+ */
+#define PADDING 0
+#define HA 1 /* host address */
+#define NA 2 /* network address */
+#define LP 3 /* leap (print in binary) */
+#define RF 4 /* refid (sometimes string, sometimes not) */
+#define AU 5 /* array of unsigned times */
+#define FX 6 /* test flags */
+#define TS 7 /* l_fp timestamp in hex */
+#define OC 8 /* integer, print in octal */
+#define AS 9 /* array of signed times */
+#define SN 10 /* signed number: must display +/- sign */
+#define EOV 255 /* end of table */
+
+/*
+ * For the most part ntpq simply displays what ntpd provides in the
+ * mostly plain-text mode 6 responses. A few variable names are by
+ * default "cooked" to provide more human-friendly output.
+ */
+const var_format cookedvars[] = {
+ { "leap", LP },
+ { "reach", OC },
+ { "refid", RF },
+ { "reftime", TS },
+ { "clock", TS },
+ { "org", TS },
+ { "rec", TS },
+ { "xmt", TS },
+ { "flash", FX },
+ { "srcadr", HA },
+ { "peeradr", HA }, /* compat with others */
+ { "dstadr", NA },
+ { "filtdelay", AU },
+ { "filtoffset", AS },
+ { "filtdisp", AU },
+ { "filterror", AU }, /* compat with others */
+ { "offset", SN },
+ { "frequency", SN }
+};
+
+
+
+/*
+ * flasher bits
+ */
+static const char *tstflagnames[] = {
+ "pkt_dup", /* TEST1 */
+ "pkt_bogus", /* TEST2 */
+ "pkt_unsync", /* TEST3 */
+ "pkt_denied", /* TEST4 */
+ "pkt_auth", /* TEST5 */
+ "pkt_stratum", /* TEST6 */
+ "pkt_header", /* TEST7 */
+ "pkt_autokey", /* TEST8 */
+ "pkt_crypto", /* TEST9 */
+ "peer_stratum", /* TEST10 */
+ "peer_dist", /* TEST11 */
+ "peer_loop", /* TEST12 */
+ "peer_unreach" /* TEST13 */
+};
+
+
+int ntpqmain (int, char **);
+/*
+ * Built in command handler declarations
+ */
+static int openhost (const char *, int);
+static void dump_hex_printable(const void *, size_t);
+static int sendpkt (void *, size_t);
+static int getresponse (int, int, u_short *, size_t *, const char **, int);
+static int sendrequest (int, associd_t, int, size_t, const char *);
+static char * tstflags (u_long);
+#ifndef BUILD_AS_LIB
+static void getcmds (void);
+#ifndef SYS_WINNT
+static int abortcmd (void);
+#endif /* SYS_WINNT */
+static void docmd (const char *);
+static void tokenize (const char *, char **, int *);
+static int getarg (const char *, int, arg_v *);
+#endif /* BUILD_AS_LIB */
+static int findcmd (const char *, struct xcmd *,
+ struct xcmd *, struct xcmd **);
+static int rtdatetolfp (char *, l_fp *);
+static int decodearr (char *, int *, l_fp *, int);
+static void help (struct parse *, FILE *);
+static int helpsort (const void *, const void *);
+static void printusage (struct xcmd *, FILE *);
+static void timeout (struct parse *, FILE *);
+static void auth_delay (struct parse *, FILE *);
+static void host (struct parse *, FILE *);
+static void ntp_poll (struct parse *, FILE *);
+static void keyid (struct parse *, FILE *);
+static void keytype (struct parse *, FILE *);
+static void passwd (struct parse *, FILE *);
+static void hostnames (struct parse *, FILE *);
+static void setdebug (struct parse *, FILE *);
+static void quit (struct parse *, FILE *);
+static void showdrefid (struct parse *, FILE *);
+static void version (struct parse *, FILE *);
+static void raw (struct parse *, FILE *);
+static void cooked (struct parse *, FILE *);
+static void authenticate (struct parse *, FILE *);
+static void ntpversion (struct parse *, FILE *);
+static void warning (const char *, ...) NTP_PRINTF(1, 2);
+static void error (const char *, ...) NTP_PRINTF(1, 2);
+static u_long getkeyid (const char *);
+static void atoascii (const char *, size_t, char *, size_t);
+static void cookedprint (int, size_t, const char *, int, int, FILE *);
+static void rawprint (int, size_t, const char *, int, int, FILE *);
+static void startoutput (void);
+static void output (FILE *, const char *, const char *);
+static void endoutput (FILE *);
+static void outputarr (FILE *, char *, int, l_fp *, int);
+static int assoccmp (const void *, const void *);
+ u_short varfmt (const char *);
+ void ntpq_custom_opt_handler(tOptions *, tOptDesc *);
+
+#ifndef BUILD_AS_LIB
+static char *list_digest_names(void);
+static char *insert_cmac (char *list);
+static void on_ctrlc (void);
+static int my_easprintf (char**, const char *, ...) NTP_PRINTF(2, 3);
+# if defined(OPENSSL) && defined(HAVE_EVP_MD_DO_ALL_SORTED)
+static void list_md_fn (const EVP_MD *m, const char *from,
+ const char *to, void *arg);
+# endif /* defined(OPENSSL) && defined(HAVE_EVP_MD_DO_ALL_SORTED) */
+#endif /* !defined(BUILD_AS_LIB) */
+
+
+/* read a character from memory and expand to integer */
+static inline int
+pgetc(
+ const char *cp
+ )
+{
+ return (int)*(const unsigned char*)cp;
+}
+
+
+
+/*
+ * Built-in commands we understand
+ */
+struct xcmd builtins[] = {
+ { "?", help, { OPT|NTP_STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "help", help, { OPT|NTP_STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "timeout", timeout, { OPT|NTP_UINT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the primary receive time out" },
+ { "delay", auth_delay, { OPT|NTP_INT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the delay added to encryption time stamps" },
+ { "host", host, { OPT|NTP_STR, OPT|NTP_STR, NO, NO },
+ { "-4|-6", "hostname", "", "" },
+ "specify the host whose NTP server we talk to" },
+ { "poll", ntp_poll, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
+ { "n", "verbose", "", "" },
+ "poll an NTP server in client mode `n' times" },
+ { "passwd", passwd, { OPT|NTP_STR, NO, NO, NO },
+ { "", "", "", "" },
+ "specify a password to use for authenticated requests"},
+ { "hostnames", hostnames, { OPT|NTP_STR, NO, NO, NO },
+ { "yes|no", "", "", "" },
+ "specify whether hostnames or net numbers are printed"},
+ { "debug", setdebug, { OPT|NTP_STR, NO, NO, NO },
+ { "no|more|less", "", "", "" },
+ "set/change debugging level" },
+ { "quit", quit, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "exit ntpq" },
+ { "exit", quit, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "exit ntpq" },
+ { "keyid", keyid, { OPT|NTP_UINT, NO, NO, NO },
+ { "key#", "", "", "" },
+ "set keyid to use for authenticated requests" },
+ { "drefid", showdrefid, { OPT|NTP_STR, NO, NO, NO },
+ { "hash|ipv4", "", "", "" },
+ "display refid's as IPv4 or hash" },
+ { "version", version, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print version number" },
+ { "raw", raw, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "do raw mode variable output" },
+ { "cooked", cooked, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "do cooked mode variable output" },
+ { "authenticate", authenticate, { OPT|NTP_STR, NO, NO, NO },
+ { "yes|no", "", "", "" },
+ "always authenticate requests to this server" },
+ { "ntpversion", ntpversion, { OPT|NTP_UINT, NO, NO, NO },
+ { "version number", "", "", "" },
+ "set the NTP version number to use for requests" },
+ { "keytype", keytype, { OPT|NTP_STR, NO, NO, NO },
+ { "key type %s", "", "", "" },
+ NULL },
+ { 0, 0, { NO, NO, NO, NO },
+ { "", "", "", "" }, "" }
+};
+
+
+/*
+ * Default values we use.
+ */
+#define DEFHOST "localhost" /* default host name */
+#define DEFTIMEOUT 5 /* wait 5 seconds for 1st pkt */
+#define DEFSTIMEOUT 3 /* and 3 more for each additional */
+/*
+ * Requests are automatically retried once, so total timeout with no
+ * response is a bit over 2 * DEFTIMEOUT, or 10 seconds. At the other
+ * extreme, a request eliciting 32 packets of responses each for some
+ * reason nearly DEFSTIMEOUT seconds after the prior in that series,
+ * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
+ * 93 seconds to fail each of two times, or 186 seconds.
+ * Some commands involve a series of requests, such as "peers" and
+ * "mrulist", so the cumulative timeouts are even longer for those.
+ */
+#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
+#define LENHOSTNAME 256 /* host name is 256 characters long */
+#define MAXCMDS 100 /* maximum commands on cmd line */
+#define MAXHOSTS 200 /* maximum hosts on cmd line */
+#define MAXLINE 512 /* maximum line length */
+#define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */
+#define MAXVARLEN 256 /* maximum length of a variable name */
+#define MAXVALLEN 2048 /* maximum length of a variable value */
+#define MAXOUTLINE 72 /* maximum length of an output line */
+#define SCREENWIDTH 76 /* nominal screen width in columns */
+
+/*
+ * Some variables used and manipulated locally
+ */
+struct sock_timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */
+struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
+l_fp delay_time; /* delay time */
+char currenthost[LENHOSTNAME]; /* current host name */
+int currenthostisnum; /* is prior text from IP? */
+struct sockaddr_in hostaddr; /* host address */
+int showhostnames = 1; /* show host names by default */
+int wideremote = 0; /* show wide remote names? */
+
+int ai_fam_templ; /* address family */
+int ai_fam_default; /* default address family */
+SOCKET sockfd; /* fd socket is opened on */
+int havehost = 0; /* set to 1 when host open */
+int s_port = 0;
+struct servent *server_entry = NULL; /* server entry for ntp */
+
+
+/*
+ * Sequence number used for requests. It is incremented before
+ * it is used.
+ */
+u_short sequence;
+
+/*
+ * Holds data returned from queries. Declare buffer long to be sure of
+ * alignment.
+ */
+#define DATASIZE (MAXFRAGS*480) /* maximum amount of data */
+long pktdata[DATASIZE/sizeof(long)];
+
+/*
+ * assoc_cache[] is a dynamic array which allows references to
+ * associations using &1 ... &N for n associations, avoiding manual
+ * lookup of the current association IDs for a given ntpd. It also
+ * caches the status word for each association, retrieved incidentally.
+ */
+struct association * assoc_cache;
+u_int assoc_cache_slots;/* count of allocated array entries */
+u_int numassoc; /* number of cached associations */
+
+/*
+ * For commands typed on the command line (with the -c option)
+ */
+size_t numcmds = 0;
+const char *ccmds[MAXCMDS];
+#define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
+
+/*
+ * When multiple hosts are specified.
+ */
+
+u_int numhosts;
+
+chost chosts[MAXHOSTS];
+#define ADDHOST(cp) \
+ do { \
+ if (numhosts < MAXHOSTS) { \
+ chosts[numhosts].name = (cp); \
+ chosts[numhosts].fam = ai_fam_templ; \
+ numhosts++; \
+ } \
+ } while (0)
+
+/*
+ * Macro definitions we use
+ */
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+#define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0')
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Jump buffer for longjumping back to the command level.
+ *
+ * Since we do this from a signal handler, we use 'sig{set,long}jmp()'
+ * if available. The signal is blocked by default during the excution of
+ * a signal handler, and it is unspecified if '{set,long}jmp()' save and
+ * restore the signal mask. They do on BSD, it depends on the GLIBC
+ * version on Linux, and the gods know what happens on other OSes...
+ *
+ * So we use the 'sig{set,long}jmp()' functions where available, because
+ * for them the semantics are well-defined. If we have to fall back to
+ * '{set,long}jmp()', the CTRL-C handling might be a bit erratic.
+ */
+#if HAVE_DECL_SIGSETJMP && HAVE_DECL_SIGLONGJMP
+# define JMP_BUF sigjmp_buf
+# define SETJMP(x) sigsetjmp((x), 1)
+# define LONGJMP(x, v) siglongjmp((x),(v))
+#else
+# define JMP_BUF jmp_buf
+# define SETJMP(x) setjmp((x))
+# define LONGJMP(x, v) longjmp((x),(v))
+#endif
+static JMP_BUF interrupt_buf;
+static volatile int jump = 0;
+
+/*
+ * Points at file being currently printed into
+ */
+FILE *current_output = NULL;
+
+/*
+ * Command table imported from ntpdc_ops.c
+ */
+extern struct xcmd opcmds[];
+
+char const *progname;
+
+#ifdef NO_MAIN_ALLOWED
+#ifndef BUILD_AS_LIB
+CALL(ntpq,"ntpq",ntpqmain);
+
+void clear_globals(void)
+{
+ extern int ntp_optind;
+ showhostnames = 0; /* don'tshow host names by default */
+ ntp_optind = 0;
+ server_entry = NULL; /* server entry for ntp */
+ havehost = 0; /* set to 1 when host open */
+ numassoc = 0; /* number of cached associations */
+ numcmds = 0;
+ numhosts = 0;
+}
+#endif /* !BUILD_AS_LIB */
+#endif /* NO_MAIN_ALLOWED */
+
+/*
+ * main - parse arguments and handle options
+ */
+#ifndef NO_MAIN_ALLOWED
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ return ntpqmain(argc, argv);
+}
+#endif
+
+
+#ifndef BUILD_AS_LIB
+int
+ntpqmain(
+ int argc,
+ char *argv[]
+ )
+{
+ u_int ihost;
+ size_t icmd;
+
+
+#ifdef SYS_VXWORKS
+ clear_globals();
+ taskPrioritySet(taskIdSelf(), 100 );
+#endif
+
+ delay_time.l_ui = 0;
+ delay_time.l_uf = DEFDELAY;
+
+ init_lib(); /* sets up ipv4_works, ipv6_works */
+ ssl_applink();
+ init_auth();
+
+ /* Check to see if we have IPv6. Otherwise default to IPv4 */
+ if (!ipv6_works)
+ ai_fam_default = AF_INET;
+
+ /* Fixup keytype's help based on available digest names */
+
+ {
+ char *list;
+ char *msg;
+
+ list = list_digest_names();
+
+ for (icmd = 0; icmd < sizeof(builtins)/sizeof(*builtins); icmd++) {
+ if (strcmp("keytype", builtins[icmd].keyword) == 0) {
+ break;
+ }
+ }
+
+ /* CID: 1295478 */
+ /* This should only "trip" if "keytype" is removed from builtins */
+ INSIST(icmd < sizeof(builtins)/sizeof(*builtins));
+
+#ifdef OPENSSL
+ builtins[icmd].desc[0] = "digest-name";
+ my_easprintf(&msg,
+ "set key type to use for authenticated requests, one of:%s",
+ list);
+#else
+ builtins[icmd].desc[0] = "md5";
+ my_easprintf(&msg,
+ "set key type to use for authenticated requests (%s)",
+ list);
+#endif
+ builtins[icmd].comment = msg;
+ free(list);
+ }
+
+ progname = argv[0];
+
+ {
+ int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
+ argc -= optct;
+ argv += optct;
+ }
+
+ /*
+ * Process options other than -c and -p, which are specially
+ * handled by ntpq_custom_opt_handler().
+ */
+
+ debug = OPT_VALUE_SET_DEBUG_LEVEL;
+
+ if (HAVE_OPT(IPV4))
+ ai_fam_templ = AF_INET;
+ else if (HAVE_OPT(IPV6))
+ ai_fam_templ = AF_INET6;
+ else
+ ai_fam_templ = ai_fam_default;
+
+ if (HAVE_OPT(INTERACTIVE))
+ interactive = 1;
+
+ if (HAVE_OPT(NUMERIC))
+ showhostnames = 0;
+
+ if (HAVE_OPT(WIDE))
+ wideremote = 1;
+
+ old_rv = HAVE_OPT(OLD_RV);
+
+ drefid = OPT_VALUE_REFID;
+
+ if (0 == argc) {
+ ADDHOST(DEFHOST);
+ } else {
+ for (ihost = 0; ihost < (u_int)argc; ihost++) {
+ if ('-' == *argv[ihost]) {
+ //
+ // If I really cared I'd also check:
+ // 0 == argv[ihost][2]
+ //
+ // and there are other cases as well...
+ //
+ if ('4' == argv[ihost][1]) {
+ ai_fam_templ = AF_INET;
+ continue;
+ } else if ('6' == argv[ihost][1]) {
+ ai_fam_templ = AF_INET6;
+ continue;
+ } else {
+ // XXX Throw a usage error
+ }
+ }
+ ADDHOST(argv[ihost]);
+ }
+ }
+
+ if (numcmds == 0 && interactive == 0
+ && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
+ interactive = 1;
+ }
+
+ set_ctrl_c_hook(on_ctrlc);
+#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
+ if (interactive)
+ push_ctrl_c_handler(abortcmd);
+#endif /* SYS_WINNT */
+
+ if (numcmds == 0) {
+ (void) openhost(chosts[0].name, chosts[0].fam);
+ getcmds();
+ } else {
+ for (ihost = 0; ihost < numhosts; ihost++) {
+ if (openhost(chosts[ihost].name, chosts[ihost].fam)) {
+ if (ihost && current_output)
+ fputc('\n', current_output);
+ for (icmd = 0; icmd < numcmds; icmd++) {
+ if (icmd && current_output)
+ fputc('\n', current_output);
+ docmd(ccmds[icmd]);
+ }
+ }
+ }
+ }
+#ifdef SYS_WINNT
+ WSACleanup();
+#endif /* SYS_WINNT */
+ return 0;
+}
+#endif /* !BUILD_AS_LIB */
+
+/*
+ * openhost - open a socket to a host
+ */
+static int
+openhost(
+ const char *hname,
+ int fam
+ )
+{
+ const char svc[] = "ntp";
+ char temphost[LENHOSTNAME];
+ int a_info;
+ struct addrinfo hints, *ai;
+ sockaddr_u addr;
+ size_t octets;
+ const char *cp;
+ char name[LENHOSTNAME];
+
+ /*
+ * We need to get by the [] if they were entered
+ */
+ if (*hname == '[') {
+ cp = strchr(hname + 1, ']');
+ if (!cp || (octets = (size_t)(cp - hname) - 1) >= sizeof(name)) {
+ errno = EINVAL;
+ warning("%s", "bad hostname/address");
+ return 0;
+ }
+ memcpy(name, hname + 1, octets);
+ name[octets] = '\0';
+ hname = name;
+ }
+
+ /*
+ * First try to resolve it as an ip address and if that fails,
+ * do a fullblown (dns) lookup. That way we only use the dns
+ * when it is needed and work around some implementations that
+ * will return an "IPv4-mapped IPv6 address" address if you
+ * give it an IPv4 address to lookup.
+ */
+ ZERO(hints);
+ hints.ai_family = fam;
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = Z_AI_NUMERICHOST;
+ ai = NULL;
+
+ a_info = getaddrinfo(hname, svc, &hints, &ai);
+ if (a_info == EAI_NONAME
+#ifdef EAI_NODATA
+ || a_info == EAI_NODATA
+#endif
+ ) {
+ hints.ai_flags = AI_CANONNAME;
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ a_info = getaddrinfo(hname, svc, &hints, &ai);
+ }
+#ifdef AI_ADDRCONFIG
+ /* Some older implementations don't like AI_ADDRCONFIG. */
+ if (a_info == EAI_BADFLAGS) {
+ hints.ai_flags &= ~AI_ADDRCONFIG;
+ a_info = getaddrinfo(hname, svc, &hints, &ai);
+ }
+#endif
+ if (a_info != 0) {
+ fprintf(stderr, "%s\n", gai_strerror(a_info));
+ return 0;
+ }
+
+ INSIST(ai != NULL);
+ ZERO(addr);
+ octets = min(sizeof(addr), ai->ai_addrlen);
+ memcpy(&addr, ai->ai_addr, octets);
+
+ if (ai->ai_canonname == NULL) {
+ strlcpy(temphost, stoa(&addr), sizeof(temphost));
+ currenthostisnum = TRUE;
+ } else {
+ strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
+ currenthostisnum = FALSE;
+ }
+
+ if (debug > 2)
+ printf("Opening host %s (%s)\n",
+ temphost,
+ (ai->ai_family == AF_INET)
+ ? "AF_INET"
+ : (ai->ai_family == AF_INET6)
+ ? "AF_INET6"
+ : "AF-???"
+ );
+
+ if (havehost == 1) {
+ if (debug > 2)
+ printf("Closing old host %s\n", currenthost);
+ closesocket(sockfd);
+ havehost = 0;
+ }
+ strlcpy(currenthost, temphost, sizeof(currenthost));
+
+ /* port maps to the same location in both families */
+ s_port = NSRCPORT(&addr);
+#ifdef SYS_VXWORKS
+ ((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
+ if (ai->ai_family == AF_INET)
+ *(struct sockaddr_in *)&hostaddr=
+ *((struct sockaddr_in *)ai->ai_addr);
+ else
+ *(struct sockaddr_in6 *)&hostaddr=
+ *((struct sockaddr_in6 *)ai->ai_addr);
+#endif /* SYS_VXWORKS */
+
+#ifdef SYS_WINNT
+ {
+ int optionValue = SO_SYNCHRONOUS_NONALERT;
+ int err;
+
+ err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
+ (void *)&optionValue, sizeof(optionValue));
+ if (err) {
+ mfprintf(stderr,
+ "setsockopt(SO_SYNCHRONOUS_NONALERT)"
+ " error: %m\n");
+ freeaddrinfo(ai);
+ exit(1);
+ }
+ }
+#endif /* SYS_WINNT */
+
+ sockfd = socket(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol);
+ if (sockfd == INVALID_SOCKET) {
+ error("socket");
+ freeaddrinfo(ai);
+ return 0;
+ }
+
+
+#ifdef NEED_RCVBUF_SLOP
+# ifdef SO_RCVBUF
+ { int rbufsize = DATASIZE + 2048; /* 2K for slop */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
+ (void *)&rbufsize, sizeof(int)) == -1)
+ error("setsockopt");
+ }
+# endif
+#endif
+
+ if
+#ifdef SYS_VXWORKS
+ (connect(sockfd, (struct sockaddr *)&hostaddr,
+ sizeof(hostaddr)) == -1)
+#else
+ (connect(sockfd, (struct sockaddr *)ai->ai_addr,
+ ai->ai_addrlen) == -1)
+#endif /* SYS_VXWORKS */
+ {
+ error("connect");
+ freeaddrinfo(ai);
+ return 0;
+ }
+ freeaddrinfo(ai);
+ havehost = 1;
+ numassoc = 0;
+
+ return 1;
+}
+
+
+static void
+dump_hex_printable(
+ const void * data,
+ size_t len
+ )
+{
+ /* every line shows at most 16 bytes, so we need a buffer of
+ * 4 * 16 (2 xdigits, 1 char, one sep for xdigits)
+ * + 2 * 1 (block separators)
+ * + <LF> + <NUL>
+ *---------------
+ * 68 bytes
+ */
+ static const char s_xdig[16] = "0123456789ABCDEF";
+
+ char lbuf[68];
+ int ch, rowlen;
+ const u_char * cdata = data;
+ char *xptr, *pptr;
+
+ while (len) {
+ memset(lbuf, ' ', sizeof(lbuf));
+ xptr = lbuf;
+ pptr = lbuf + 3*16 + 2;
+
+ rowlen = (len > 16) ? 16 : (int)len;
+ len -= rowlen;
+
+ do {
+ ch = *cdata++;
+
+ *xptr++ = s_xdig[ch >> 4 ];
+ *xptr++ = s_xdig[ch & 0x0F];
+ if (++xptr == lbuf + 3*8)
+ ++xptr;
+
+ *pptr++ = isprint(ch) ? (char)ch : '.';
+ } while (--rowlen);
+
+ *pptr++ = '\n';
+ *pptr = '\0';
+ fputs(lbuf, stdout);
+ }
+}
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the remote host
+ */
+static int
+sendpkt(
+ void * xdata,
+ size_t xdatalen
+ )
+{
+ if (debug >= 3)
+ printf("Sending %zu octets\n", xdatalen);
+
+ if (send(sockfd, xdata, xdatalen, 0) == -1) {
+ warning("write to %s failed", currenthost);
+ return -1;
+ }
+
+ if (debug >= 4) {
+ printf("Request packet:\n");
+ dump_hex_printable(xdata, xdatalen);
+ }
+ return 0;
+}
+
+/*
+ * getresponse - get a (series of) response packet(s) and return the data
+ */
+static int
+getresponse(
+ int opcode,
+ int associd,
+ u_short *rstatus,
+ size_t *rsize,
+ const char **rdata,
+ int timeo
+ )
+{
+ struct ntp_control rpkt;
+ struct sock_timeval tvo;
+ u_short offsets[MAXFRAGS+1];
+ u_short counts[MAXFRAGS+1];
+ u_short offset;
+ u_short count;
+ size_t numfrags;
+ size_t f;
+ size_t ff;
+ int seenlastfrag;
+ int shouldbesize;
+ fd_set fds;
+ int n;
+ int errcode;
+ /* absolute timeout checks. Not 'time_t' by intention! */
+ uint32_t tobase; /* base value for timeout */
+ uint32_t tospan; /* timeout span (max delay) */
+ uint32_t todiff; /* current delay */
+
+ memset(offsets, 0, sizeof(offsets));
+ memset(counts , 0, sizeof(counts ));
+
+ /*
+ * This is pretty tricky. We may get between 1 and MAXFRAG packets
+ * back in response to the request. We peel the data out of
+ * each packet and collect it in one long block. When the last
+ * packet in the sequence is received we'll know how much data we
+ * should have had. Note we use one long time out, should reconsider.
+ */
+ *rsize = 0;
+ if (rstatus)
+ *rstatus = 0;
+ *rdata = (char *)pktdata;
+
+ numfrags = 0;
+ seenlastfrag = 0;
+
+ tobase = (uint32_t)time(NULL);
+
+ FD_ZERO(&fds);
+
+ /*
+ * Loop until we have an error or a complete response. Nearly all
+ * code paths to loop again use continue.
+ */
+ for (;;) {
+
+ if (numfrags == 0)
+ tvo = tvout;
+ else
+ tvo = tvsout;
+ tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
+
+ FD_SET(sockfd, &fds);
+ n = select(sockfd+1, &fds, NULL, NULL, &tvo);
+ if (n == -1) {
+#if !defined(SYS_WINNT) && defined(EINTR)
+ /* Windows does not know about EINTR (until very
+ * recently) and the handling of console events
+ * is *very* different from POSIX/UNIX signal
+ * handling anyway.
+ *
+ * Under non-windows targets we map EINTR as
+ * 'last packet was received' and try to exit
+ * the receive sequence.
+ */
+ if (errno == EINTR) {
+ seenlastfrag = 1;
+ goto maybe_final;
+ }
+#endif
+ warning("select fails");
+ return -1;
+ }
+
+ /*
+ * Check if this is already too late. Trash the data and
+ * fake a timeout if this is so.
+ */
+ todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
+ if ((n > 0) && (todiff > tospan)) {
+ n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
+ n -= n; /* faked timeout return from 'select()',
+ * execute RMW cycle on 'n'
+ */
+ }
+
+ if (n <= 0) {
+ /*
+ * Timed out. Return what we have
+ */
+ if (numfrags == 0) {
+ if (timeo)
+ fprintf(stderr,
+ "%s: timed out, nothing received\n",
+ currenthost);
+ return ERR_TIMEOUT;
+ }
+ if (timeo)
+ fprintf(stderr,
+ "%s: timed out with incomplete data\n",
+ currenthost);
+ if (debug) {
+ fprintf(stderr,
+ "ERR_INCOMPLETE: Received fragments:\n");
+ for (f = 0; f < numfrags; f++)
+ fprintf(stderr,
+ "%2u: %5d %5d\t%3d octets\n",
+ (u_int)f, offsets[f],
+ offsets[f] +
+ counts[f],
+ counts[f]);
+ fprintf(stderr,
+ "last fragment %sreceived\n",
+ (seenlastfrag)
+ ? ""
+ : "not ");
+ }
+ return ERR_INCOMPLETE;
+ }
+
+ n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
+ if (n < 0) {
+ warning("read");
+ return -1;
+ }
+
+ if (debug >= 4) {
+ printf("Response packet:\n");
+ dump_hex_printable(&rpkt, n);
+ }
+
+ /*
+ * Check for format errors. Bug proofing.
+ */
+ if (n < (int)CTL_HEADER_LEN) {
+ if (debug)
+ printf("Short (%d byte) packet received\n", n);
+ continue;
+ }
+ if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
+ || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
+ if (debug)
+ printf("Packet received with version %d\n",
+ PKT_VERSION(rpkt.li_vn_mode));
+ continue;
+ }
+ if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
+ if (debug)
+ printf("Packet received with mode %d\n",
+ PKT_MODE(rpkt.li_vn_mode));
+ continue;
+ }
+ if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
+ if (debug)
+ printf("Received request packet, wanted response\n");
+ continue;
+ }
+
+ /*
+ * Check opcode and sequence number for a match.
+ * Could be old data getting to us.
+ */
+ if (ntohs(rpkt.sequence) != sequence) {
+ if (debug)
+ printf("Received sequnce number %d, wanted %d\n",
+ ntohs(rpkt.sequence), sequence);
+ continue;
+ }
+ if (CTL_OP(rpkt.r_m_e_op) != opcode) {
+ if (debug)
+ printf(
+ "Received opcode %d, wanted %d (sequence number okay)\n",
+ CTL_OP(rpkt.r_m_e_op), opcode);
+ continue;
+ }
+
+ /*
+ * Check the error code. If non-zero, return it.
+ */
+ if (CTL_ISERROR(rpkt.r_m_e_op)) {
+ errcode = (ntohs(rpkt.status) >> 8) & 0xff;
+ if (CTL_ISMORE(rpkt.r_m_e_op))
+ TRACE(1, ("Error code %d received on not-final packet\n",
+ errcode));
+ if (errcode == CERR_UNSPEC)
+ return ERR_UNSPEC;
+ return errcode;
+ }
+
+ /*
+ * Check the association ID to make sure it matches what
+ * we sent.
+ */
+ if (ntohs(rpkt.associd) != associd) {
+ TRACE(1, ("Association ID %d doesn't match expected %d\n",
+ ntohs(rpkt.associd), associd));
+ /*
+ * Hack for silly fuzzballs which, at the time of writing,
+ * return an assID of sys.peer when queried for system variables.
+ */
+#ifdef notdef
+ continue;
+#endif
+ }
+
+ /*
+ * Collect offset and count. Make sure they make sense.
+ */
+ offset = ntohs(rpkt.offset);
+ count = ntohs(rpkt.count);
+
+ /*
+ * validate received payload size is padded to next 32-bit
+ * boundary and no smaller than claimed by rpkt.count
+ */
+ if (n & 0x3) {
+ TRACE(1, ("Response packet not padded, size = %d\n",
+ n));
+ continue;
+ }
+
+ shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
+
+ if (n < shouldbesize) {
+ printf("Response packet claims %u octets payload, above %ld received\n",
+ count, (long)(n - CTL_HEADER_LEN));
+ return ERR_INCOMPLETE;
+ }
+
+ if (debug >= 3 && shouldbesize > n) {
+ u_int32 key;
+ u_int32 *lpkt;
+ int maclen;
+
+ /*
+ * Usually we ignore authentication, but for debugging purposes
+ * we watch it here.
+ */
+ /* round to 8 octet boundary */
+ shouldbesize = (shouldbesize + 7) & ~7;
+
+ maclen = n - shouldbesize;
+ if (maclen >= (int)MIN_MAC_LEN) {
+ printf(
+ "Packet shows signs of authentication (total %d, data %d, mac %d)\n",
+ n, shouldbesize, maclen);
+ lpkt = (u_int32 *)&rpkt;
+ printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
+ key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
+ printf("Authenticated with keyid %lu\n", (u_long)key);
+ if (key != 0 && key != info_auth_keyid) {
+ printf("We don't know that key\n");
+ } else {
+ if (authdecrypt(key, (u_int32 *)&rpkt,
+ n - maclen, maclen)) {
+ printf("Auth okay!\n");
+ } else {
+ printf("Auth failed!\n");
+ }
+ }
+ }
+ }
+
+ TRACE(2, ("Got packet, size = %d\n", n));
+ if (count > (n - CTL_HEADER_LEN)) {
+ TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
+ count, (long)n - CTL_HEADER_LEN));
+ continue;
+ }
+ if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
+ TRACE(1, ("Received count of 0 in non-final fragment\n"));
+ continue;
+ }
+ if (offset + count > sizeof(pktdata)) {
+ TRACE(1, ("Offset %u, count %u, too big for buffer\n",
+ offset, count));
+ return ERR_TOOMUCH;
+ }
+ if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
+ TRACE(1, ("Received second last fragment packet\n"));
+ continue;
+ }
+
+ /*
+ * So far, so good. Record this fragment, making sure it doesn't
+ * overlap anything.
+ */
+ TRACE(2, ("Packet okay\n"));
+
+ if (numfrags > (MAXFRAGS - 1)) {
+ TRACE(2, ("Number of fragments exceeds maximum %d\n",
+ MAXFRAGS - 1));
+ return ERR_TOOMUCH;
+ }
+
+ /*
+ * Find the position for the fragment relative to any
+ * previously received.
+ */
+ for (f = 0;
+ f < numfrags && offsets[f] < offset;
+ f++) {
+ /* empty body */ ;
+ }
+
+ if (f < numfrags && offset == offsets[f]) {
+ TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
+ count, offset, counts[f], offsets[f]));
+ continue;
+ }
+
+ if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
+ TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
+ offset, counts[f-1], offsets[f-1]));
+ continue;
+ }
+
+ if (f < numfrags && (offset + count) > offsets[f]) {
+ TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
+ count, offset, offsets[f]));
+ continue;
+ }
+
+ for (ff = numfrags; ff > f; ff--) {
+ offsets[ff] = offsets[ff-1];
+ counts[ff] = counts[ff-1];
+ }
+ offsets[f] = offset;
+ counts[f] = count;
+ numfrags++;
+
+ /*
+ * Got that stuffed in right. Figure out if this was the last.
+ * Record status info out of the last packet.
+ */
+ if (!CTL_ISMORE(rpkt.r_m_e_op)) {
+ seenlastfrag = 1;
+ if (rstatus != 0)
+ *rstatus = ntohs(rpkt.status);
+ }
+
+ /*
+ * Copy the data into the data buffer, and bump the
+ * timout base in case we need more.
+ */
+ memcpy((char *)pktdata + offset, &rpkt.u, count);
+ tobase = (uint32_t)time(NULL);
+
+ /*
+ * If we've seen the last fragment, look for holes in the sequence.
+ * If there aren't any, we're done.
+ */
+#if !defined(SYS_WINNT) && defined(EINTR)
+ maybe_final:
+#endif
+
+ if (seenlastfrag && offsets[0] == 0) {
+ for (f = 1; f < numfrags; f++)
+ if (offsets[f-1] + counts[f-1] !=
+ offsets[f])
+ break;
+ if (f == numfrags) {
+ *rsize = offsets[f-1] + counts[f-1];
+ TRACE(1, ("%lu packets reassembled into response\n",
+ (u_long)numfrags));
+ return 0;
+ }
+ }
+ } /* giant for (;;) collecting response packets */
+} /* getresponse() */
+
+
+/*
+ * sendrequest - format and send a request packet
+ */
+static int
+sendrequest(
+ int opcode,
+ associd_t associd,
+ int auth,
+ size_t qsize,
+ const char *qdata
+ )
+{
+ struct ntp_control qpkt;
+ size_t pktsize;
+ u_long key_id;
+ char * pass;
+ size_t maclen;
+
+ /*
+ * Check to make sure the data will fit in one packet
+ */
+ if (qsize > CTL_MAX_DATA_LEN) {
+ fprintf(stderr,
+ "***Internal error! qsize (%zu) too large\n",
+ qsize);
+ return 1;
+ }
+
+ /*
+ * Fill in the packet
+ */
+ qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
+ qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
+ qpkt.sequence = htons(sequence);
+ qpkt.status = 0;
+ qpkt.associd = htons((u_short)associd);
+ qpkt.offset = 0;
+ qpkt.count = htons((u_short)qsize);
+
+ pktsize = CTL_HEADER_LEN;
+
+ /*
+ * If we have data, copy and pad it out to a 32-bit boundary.
+ */
+ if (qsize > 0) {
+ memcpy(&qpkt.u, qdata, (size_t)qsize);
+ pktsize += qsize;
+ while (pktsize & (sizeof(u_int32) - 1)) {
+ qpkt.u.data[qsize++] = 0;
+ pktsize++;
+ }
+ }
+
+ /*
+ * If it isn't authenticated we can just send it. Otherwise
+ * we're going to have to think about it a little.
+ */
+ if (!auth && !always_auth) {
+ return sendpkt(&qpkt, pktsize);
+ }
+
+ /*
+ * Pad out packet to a multiple of 8 octets to be sure
+ * receiver can handle it.
+ */
+ while (pktsize & 7) {
+ qpkt.u.data[qsize++] = 0;
+ pktsize++;
+ }
+
+ /*
+ * Get the keyid and the password if we don't have one.
+ */
+ if (info_auth_keyid == 0) {
+ key_id = getkeyid("Keyid: ");
+ if (key_id == 0 || key_id > NTP_MAXKEY) {
+ fprintf(stderr,
+ "Invalid key identifier\n");
+ return 1;
+ }
+ info_auth_keyid = key_id;
+ }
+ if (!authistrusted(info_auth_keyid)) {
+ pass = getpass_keytype(info_auth_keytype);
+ if ('\0' == pass[0]) {
+ fprintf(stderr, "Invalid password\n");
+ return 1;
+ }
+ authusekey(info_auth_keyid, info_auth_keytype,
+ (u_char *)pass);
+ authtrust(info_auth_keyid, 1);
+ }
+
+ /*
+ * Do the encryption.
+ */
+ maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
+ if (!maclen) {
+ fprintf(stderr, "Key not found\n");
+ return 1;
+ } else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
+ fprintf(stderr,
+ "%zu octet MAC, %zu expected with %zu octet digest\n",
+ maclen, (info_auth_hashlen + sizeof(keyid_t)),
+ info_auth_hashlen);
+ return 1;
+ }
+
+ return sendpkt((char *)&qpkt, pktsize + maclen);
+}
+
+
+/*
+ * show_error_msg - display the error text for a mode 6 error response.
+ */
+void
+show_error_msg(
+ int m6resp,
+ associd_t associd
+ )
+{
+ if (numhosts > 1)
+ fprintf(stderr, "server=%s ", currenthost);
+
+ switch (m6resp) {
+
+ case CERR_BADFMT:
+ fprintf(stderr,
+ "***Server reports a bad format request packet\n");
+ break;
+
+ case CERR_PERMISSION:
+ fprintf(stderr,
+ "***Server disallowed request (authentication?)\n");
+ break;
+
+ case CERR_BADOP:
+ fprintf(stderr,
+ "***Server reports a bad opcode in request\n");
+ break;
+
+ case CERR_BADASSOC:
+ fprintf(stderr,
+ "***Association ID %d unknown to server\n",
+ associd);
+ break;
+
+ case CERR_UNKNOWNVAR:
+ fprintf(stderr,
+ "***A request variable unknown to the server\n");
+ break;
+
+ case CERR_BADVALUE:
+ fprintf(stderr,
+ "***Server indicates a request variable was bad\n");
+ break;
+
+ case ERR_UNSPEC:
+ fprintf(stderr,
+ "***Server returned an unspecified error\n");
+ break;
+
+ case ERR_TIMEOUT:
+ fprintf(stderr, "***Request timed out\n");
+ break;
+
+ case ERR_INCOMPLETE:
+ fprintf(stderr,
+ "***Response from server was incomplete\n");
+ break;
+
+ case ERR_TOOMUCH:
+ fprintf(stderr,
+ "***Buffer size exceeded for returned data\n");
+ break;
+
+ default:
+ fprintf(stderr,
+ "***Server returns unknown error code %d\n",
+ m6resp);
+ }
+}
+
+/*
+ * doquery - send a request and process the response, displaying
+ * error messages for any error responses.
+ */
+int
+doquery(
+ int opcode,
+ associd_t associd,
+ int auth,
+ size_t qsize,
+ const char *qdata,
+ u_short *rstatus,
+ size_t *rsize,
+ const char **rdata
+ )
+{
+ return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
+ rsize, rdata, FALSE);
+}
+
+
+/*
+ * doqueryex - send a request and process the response, optionally
+ * displaying error messages for any error responses.
+ */
+int
+doqueryex(
+ int opcode,
+ associd_t associd,
+ int auth,
+ size_t qsize,
+ const char *qdata,
+ u_short *rstatus,
+ size_t *rsize,
+ const char **rdata,
+ int quiet
+ )
+{
+ int res;
+ int done;
+
+ /*
+ * Check to make sure host is open
+ */
+ if (!havehost) {
+ fprintf(stderr, "***No host open, use `host' command\n");
+ return -1;
+ }
+
+ done = 0;
+ sequence++;
+
+ again:
+ /*
+ * send a request
+ */
+ res = sendrequest(opcode, associd, auth, qsize, qdata);
+ if (res != 0)
+ return res;
+
+ /*
+ * Get the response. If we got a standard error, print a message
+ */
+ res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
+
+ if (res > 0) {
+ if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
+ if (res == ERR_INCOMPLETE) {
+ /*
+ * better bump the sequence so we don't
+ * get confused about differing fragments.
+ */
+ sequence++;
+ }
+ done = 1;
+ goto again;
+ }
+ if (!quiet)
+ show_error_msg(res, associd);
+
+ }
+ return res;
+}
+
+
+#ifndef BUILD_AS_LIB
+/*
+ * getcmds - read commands from the standard input and execute them
+ */
+static void
+getcmds(void)
+{
+ char * line;
+ int count;
+
+ ntp_readline_init(interactive ? prompt : NULL);
+
+ for (;;) {
+ line = ntp_readline(&count);
+ if (NULL == line)
+ break;
+ docmd(line);
+ free(line);
+ }
+
+ ntp_readline_uninit();
+}
+#endif /* !BUILD_AS_LIB */
+
+
+#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
+/*
+ * abortcmd - catch interrupts and abort the current command
+ */
+static int
+abortcmd(void)
+{
+ if (current_output == stdout)
+ (void) fflush(stdout);
+ putc('\n', stderr);
+ (void) fflush(stderr);
+ if (jump) {
+ jump = 0;
+ LONGJMP(interrupt_buf, 1);
+ }
+ return TRUE;
+}
+#endif /* !SYS_WINNT && !BUILD_AS_LIB */
+
+
+#ifndef BUILD_AS_LIB
+/*
+ * docmd - decode the command line and execute a command
+ */
+static void
+docmd(
+ const char *cmdline
+ )
+{
+ char *tokens[1+MAXARGS+2];
+ struct parse pcmd;
+ int ntok;
+ static int i;
+ struct xcmd *xcmd;
+
+ /*
+ * Tokenize the command line. If nothing on it, return.
+ */
+ tokenize(cmdline, tokens, &ntok);
+ if (ntok == 0)
+ return;
+
+ /*
+ * Find the appropriate command description.
+ */
+ i = findcmd(tokens[0], builtins, opcmds, &xcmd);
+ if (i == 0) {
+ (void) fprintf(stderr, "***Command `%s' unknown\n",
+ tokens[0]);
+ return;
+ } else if (i >= 2) {
+ (void) fprintf(stderr, "***Command `%s' ambiguous\n",
+ tokens[0]);
+ return;
+ }
+
+ /* Warn about ignored extra args */
+ for (i = MAXARGS + 1; i < ntok ; ++i) {
+ fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
+ }
+
+ /*
+ * Save the keyword, then walk through the arguments, interpreting
+ * as we go.
+ */
+ pcmd.keyword = tokens[0];
+ pcmd.nargs = 0;
+ for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
+ if ((i+1) >= ntok) {
+ if (!(xcmd->arg[i] & OPT)) {
+ printusage(xcmd, stderr);
+ return;
+ }
+ break;
+ }
+ if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
+ break;
+ if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
+ return;
+ pcmd.nargs++;
+ }
+
+ i++;
+ if (i < ntok && *tokens[i] == '>') {
+ char *fname;
+
+ if (*(tokens[i]+1) != '\0')
+ fname = tokens[i]+1;
+ else if ((i+1) < ntok)
+ fname = tokens[i+1];
+ else {
+ (void) fprintf(stderr, "***No file for redirect\n");
+ return;
+ }
+
+ current_output = fopen(fname, "w");
+ if (current_output == NULL) {
+ (void) fprintf(stderr, "***Error opening %s: ", fname);
+ perror("");
+ return;
+ }
+ } else {
+ current_output = stdout;
+ }
+
+ if (interactive) {
+ if ( ! SETJMP(interrupt_buf)) {
+ jump = 1;
+ (xcmd->handler)(&pcmd, current_output);
+ jump = 0;
+ } else {
+ fflush(current_output);
+ fputs("\n >>> command aborted <<<\n", stderr);
+ fflush(stderr);
+ }
+
+ } else {
+ jump = 0;
+ (xcmd->handler)(&pcmd, current_output);
+ }
+ if ((NULL != current_output) && (stdout != current_output)) {
+ (void)fclose(current_output);
+ current_output = NULL;
+ }
+}
+
+
+/*
+ * tokenize - turn a command line into tokens
+ *
+ * SK: Modified to allow a quoted string
+ *
+ * HMS: If the first character of the first token is a ':' then (after
+ * eating inter-token whitespace) the 2nd token is the rest of the line.
+ */
+
+static void
+tokenize(
+ const char *line,
+ char **tokens,
+ int *ntok
+ )
+{
+ register const char *cp;
+ register char *sp;
+ static char tspace[MAXLINE];
+
+ sp = tspace;
+ cp = line;
+ for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
+ tokens[*ntok] = sp;
+
+ /* Skip inter-token whitespace */
+ while (ISSPACE(*cp))
+ cp++;
+
+ /* If we're at EOL we're done */
+ if (ISEOL(*cp))
+ break;
+
+ /* If this is the 2nd token and the first token begins
+ * with a ':', then just grab to EOL.
+ */
+
+ if (*ntok == 1 && tokens[0][0] == ':') {
+ do {
+ if (sp - tspace >= MAXLINE)
+ goto toobig;
+ *sp++ = *cp++;
+ } while (!ISEOL(*cp));
+ }
+
+ /* Check if this token begins with a double quote.
+ * If yes, continue reading till the next double quote
+ */
+ else if (*cp == '\"') {
+ ++cp;
+ do {
+ if (sp - tspace >= MAXLINE)
+ goto toobig;
+ *sp++ = *cp++;
+ } while ((*cp != '\"') && !ISEOL(*cp));
+ /* HMS: a missing closing " should be an error */
+ }
+ else {
+ do {
+ if (sp - tspace >= MAXLINE)
+ goto toobig;
+ *sp++ = *cp++;
+ } while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
+ /* HMS: Why check for a " in the previous line? */
+ }
+
+ if (sp - tspace >= MAXLINE)
+ goto toobig;
+ *sp++ = '\0';
+ }
+ return;
+
+ toobig:
+ *ntok = 0;
+ fprintf(stderr,
+ "***Line `%s' is too big\n",
+ line);
+ return;
+}
+
+
+/*
+ * getarg - interpret an argument token
+ */
+static int
+getarg(
+ const char *str,
+ int code,
+ arg_v *argp
+ )
+{
+ u_long ul;
+
+ switch (code & ~OPT) {
+ case NTP_STR:
+ argp->string = str;
+ break;
+
+ case NTP_ADD:
+ if (!getnetnum(str, &argp->netnum, NULL, 0))
+ return 0;
+ break;
+
+ case NTP_UINT:
+ if ('&' == str[0]) {
+ if (!atouint(&str[1], &ul)) {
+ fprintf(stderr,
+ "***Association index `%s' invalid/undecodable\n",
+ str);
+ return 0;
+ }
+ if (0 == numassoc) {
+ dogetassoc(stdout);
+ if (0 == numassoc) {
+ fprintf(stderr,
+ "***No associations found, `%s' unknown\n",
+ str);
+ return 0;
+ }
+ }
+ ul = min(ul, numassoc);
+ argp->uval = assoc_cache[ul - 1].assid;
+ break;
+ }
+ if (!atouint(str, &argp->uval)) {
+ fprintf(stderr, "***Illegal unsigned value %s\n",
+ str);
+ return 0;
+ }
+ break;
+
+ case NTP_INT:
+ if (!atoint(str, &argp->ival)) {
+ fprintf(stderr, "***Illegal integer value %s\n",
+ str);
+ return 0;
+ }
+ break;
+
+ case IP_VERSION:
+ if (!strcmp("-6", str)) {
+ argp->ival = 6;
+ } else if (!strcmp("-4", str)) {
+ argp->ival = 4;
+ } else {
+ fprintf(stderr, "***Version must be either 4 or 6\n");
+ return 0;
+ }
+ break;
+ }
+
+ return 1;
+}
+#endif /* !BUILD_AS_LIB */
+
+
+/*
+ * findcmd - find a command in a command description table
+ */
+static int
+findcmd(
+ const char * str,
+ struct xcmd * clist1,
+ struct xcmd * clist2,
+ struct xcmd ** cmd
+ )
+{
+ struct xcmd *cl;
+ size_t clen;
+ int nmatch;
+ struct xcmd *nearmatch = NULL;
+ struct xcmd *clist;
+
+ clen = strlen(str);
+ nmatch = 0;
+ if (clist1 != 0)
+ clist = clist1;
+ else if (clist2 != 0)
+ clist = clist2;
+ else
+ return 0;
+
+ again:
+ for (cl = clist; cl->keyword != 0; cl++) {
+ /* do a first character check, for efficiency */
+ if (*str != *(cl->keyword))
+ continue;
+ if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
+ /*
+ * Could be extact match, could be approximate.
+ * Is exact if the length of the keyword is the
+ * same as the str.
+ */
+ if (*((cl->keyword) + clen) == '\0') {
+ *cmd = cl;
+ return 1;
+ }
+ nmatch++;
+ nearmatch = cl;
+ }
+ }
+
+ /*
+ * See if there is more to do. If so, go again. Sorry about the
+ * goto, too much looking at BSD sources...
+ */
+ if (clist == clist1 && clist2 != 0) {
+ clist = clist2;
+ goto again;
+ }
+
+ /*
+ * If we got extactly 1 near match, use it, else return number
+ * of matches.
+ */
+ if (nmatch == 1) {
+ *cmd = nearmatch;
+ return 1;
+ }
+ return nmatch;
+}
+
+
+/*
+ * getnetnum - given a host name, return its net number
+ * and (optional) full name
+ */
+int
+getnetnum(
+ const char *hname,
+ sockaddr_u *num,
+ char *fullhost,
+ int af
+ )
+{
+ struct addrinfo hints, *ai = NULL;
+
+ ZERO(hints);
+ hints.ai_flags = AI_CANONNAME;
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+
+ /*
+ * decodenetnum only works with addresses, but handles syntax
+ * that getaddrinfo doesn't: [2001::1]:1234
+ */
+ if (decodenetnum(hname, num)) {
+ if (fullhost != NULL)
+ getnameinfo(&num->sa, SOCKLEN(num), fullhost,
+ LENHOSTNAME, NULL, 0, 0);
+ return 1;
+ } else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
+ INSIST(sizeof(*num) >= ai->ai_addrlen);
+ memcpy(num, ai->ai_addr, ai->ai_addrlen);
+ if (fullhost != NULL) {
+ if (ai->ai_canonname != NULL)
+ strlcpy(fullhost, ai->ai_canonname,
+ LENHOSTNAME);
+ else
+ getnameinfo(&num->sa, SOCKLEN(num),
+ fullhost, LENHOSTNAME, NULL,
+ 0, 0);
+ }
+ freeaddrinfo(ai);
+ return 1;
+ }
+ fprintf(stderr, "***Can't find host %s\n", hname);
+
+ return 0;
+}
+
+
+/*
+ * nntohost - convert network number to host name. This routine enforces
+ * the showhostnames setting.
+ */
+const char *
+nntohost(
+ sockaddr_u *netnum
+ )
+{
+ return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
+}
+
+
+/*
+ * nntohost_col - convert network number to host name in fixed width.
+ * This routine enforces the showhostnames setting.
+ * When displaying hostnames longer than the width,
+ * the first part of the hostname is displayed. When
+ * displaying numeric addresses longer than the width,
+ * Such as IPv6 addresses, the caller decides whether
+ * the first or last of the numeric address is used.
+ */
+const char *
+nntohost_col(
+ sockaddr_u * addr,
+ size_t width,
+ int preserve_lowaddrbits
+ )
+{
+ const char * out;
+
+ if (!showhostnames || SOCK_UNSPEC(addr)) {
+ if (preserve_lowaddrbits)
+ out = trunc_left(stoa(addr), width);
+ else
+ out = trunc_right(stoa(addr), width);
+ } else if (ISREFCLOCKADR(addr)) {
+ out = refnumtoa(addr);
+ } else {
+ out = trunc_right(socktohost(addr), width);
+ }
+ return out;
+}
+
+
+/*
+ * nntohostp() is the same as nntohost() plus a :port suffix
+ */
+const char *
+nntohostp(
+ sockaddr_u *netnum
+ )
+{
+ const char * hostn;
+ char * buf;
+
+ if (!showhostnames || SOCK_UNSPEC(netnum))
+ return sptoa(netnum);
+ else if (ISREFCLOCKADR(netnum))
+ return refnumtoa(netnum);
+
+ hostn = socktohost(netnum);
+ LIB_GETBUF(buf);
+ snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
+
+ return buf;
+}
+
+/*
+ * rtdatetolfp - decode an RT-11 date into an l_fp
+ */
+static int
+rtdatetolfp(
+ char *str,
+ l_fp *lfp
+ )
+{
+ register char *cp;
+ register int i;
+ struct calendar cal;
+ char buf[4];
+
+ cal.yearday = 0;
+
+ /*
+ * An RT-11 date looks like:
+ *
+ * d[d]-Mth-y[y] hh:mm:ss
+ *
+ * (No docs, but assume 4-digit years are also legal...)
+ *
+ * d[d]-Mth-y[y[y[y]]] hh:mm:ss
+ */
+ cp = str;
+ if (!isdigit(pgetc(cp))) {
+ if (*cp == '-') {
+ /*
+ * Catch special case
+ */
+ L_CLR(lfp);
+ return 1;
+ }
+ return 0;
+ }
+
+ cal.monthday = (u_char) (*cp++ - '0'); /* ascii dependent */
+ if (isdigit(pgetc(cp))) {
+ cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
+ cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
+ }
+
+ if (*cp++ != '-')
+ return 0;
+
+ for (i = 0; i < 3; i++)
+ buf[i] = *cp++;
+ buf[3] = '\0';
+
+ for (i = 0; i < 12; i++)
+ if (STREQ(buf, months[i]))
+ break;
+ if (i == 12)
+ return 0;
+ cal.month = (u_char)(i + 1);
+
+ if (*cp++ != '-')
+ return 0;
+
+ if (!isdigit(pgetc(cp)))
+ return 0;
+ cal.year = (u_short)(*cp++ - '0');
+ if (isdigit(pgetc(cp))) {
+ cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
+ cal.year = (u_short)(*cp++ - '0');
+ }
+ if (isdigit(pgetc(cp))) {
+ cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
+ cal.year = (u_short)(cal.year + *cp++ - '0');
+ }
+ if (isdigit(pgetc(cp))) {
+ cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
+ cal.year = (u_short)(cal.year + *cp++ - '0');
+ }
+
+ /*
+ * Catch special case. If cal.year == 0 this is a zero timestamp.
+ */
+ if (cal.year == 0) {
+ L_CLR(lfp);
+ return 1;
+ }
+
+ if (*cp++ != ' ' || !isdigit(pgetc(cp)))
+ return 0;
+ cal.hour = (u_char)(*cp++ - '0');
+ if (isdigit(pgetc(cp))) {
+ cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
+ cal.hour = (u_char)(cal.hour + *cp++ - '0');
+ }
+
+ if (*cp++ != ':' || !isdigit(pgetc(cp)))
+ return 0;
+ cal.minute = (u_char)(*cp++ - '0');
+ if (isdigit(pgetc(cp))) {
+ cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
+ cal.minute = (u_char)(cal.minute + *cp++ - '0');
+ }
+
+ if (*cp++ != ':' || !isdigit(pgetc(cp)))
+ return 0;
+ cal.second = (u_char)(*cp++ - '0');
+ if (isdigit(pgetc(cp))) {
+ cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
+ cal.second = (u_char)(cal.second + *cp++ - '0');
+ }
+
+ /*
+ * For RT-11, 1972 seems to be the pivot year
+ */
+ if (cal.year < 72)
+ cal.year += 2000;
+ if (cal.year < 100)
+ cal.year += 1900;
+
+ lfp->l_ui = caltontp(&cal);
+ lfp->l_uf = 0;
+ return 1;
+}
+
+
+/*
+ * decodets - decode a timestamp into an l_fp format number, with
+ * consideration of fuzzball formats.
+ */
+int
+decodets(
+ char *str,
+ l_fp *lfp
+ )
+{
+ char *cp;
+ char buf[30];
+ size_t b;
+
+ /*
+ * If it starts with a 0x, decode as hex.
+ */
+ if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
+ return hextolfp(str+2, lfp);
+
+ /*
+ * If it starts with a '"', try it as an RT-11 date.
+ */
+ if (*str == '"') {
+ cp = str + 1;
+ b = 0;
+ while ('"' != *cp && '\0' != *cp &&
+ b < COUNTOF(buf) - 1)
+ buf[b++] = *cp++;
+ buf[b] = '\0';
+ return rtdatetolfp(buf, lfp);
+ }
+
+ /*
+ * Might still be hex. Check out the first character. Talk
+ * about heuristics!
+ */
+ if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
+ return hextolfp(str, lfp);
+
+ /*
+ * Try it as a decimal. If this fails, try as an unquoted
+ * RT-11 date. This code should go away eventually.
+ */
+ if (atolfp(str, lfp))
+ return 1;
+
+ return rtdatetolfp(str, lfp);
+}
+
+
+/*
+ * decodetime - decode a time value. It should be in milliseconds
+ */
+int
+decodetime(
+ char *str,
+ l_fp *lfp
+ )
+{
+ return mstolfp(str, lfp);
+}
+
+
+/*
+ * decodeint - decode an integer
+ */
+int
+decodeint(
+ char *str,
+ long *val
+ )
+{
+ if (*str == '0') {
+ if (*(str+1) == 'x' || *(str+1) == 'X')
+ return hextoint(str+2, (u_long *)val);
+ return octtoint(str, (u_long *)val);
+ }
+ return atoint(str, val);
+}
+
+
+/*
+ * decodeuint - decode an unsigned integer
+ */
+int
+decodeuint(
+ char *str,
+ u_long *val
+ )
+{
+ if (*str == '0') {
+ if (*(str + 1) == 'x' || *(str + 1) == 'X')
+ return (hextoint(str + 2, val));
+ return (octtoint(str, val));
+ }
+ return (atouint(str, val));
+}
+
+
+/*
+ * decodearr - decode an array of time values
+ */
+static int
+decodearr(
+ char *cp,
+ int *narr,
+ l_fp *lfpa,
+ int amax
+ )
+{
+ char *bp;
+ char buf[60];
+
+ *narr = 0;
+
+ while (*narr < amax && *cp) {
+ if (isspace(pgetc(cp))) {
+ do
+ ++cp;
+ while (*cp && isspace(pgetc(cp)));
+ } else {
+ bp = buf;
+ do {
+ if (bp != (buf + sizeof(buf) - 1))
+ *bp++ = *cp;
+ ++cp;
+ } while (*cp && !isspace(pgetc(cp)));
+ *bp = '\0';
+
+ if (!decodetime(buf, lfpa))
+ return 0;
+ ++(*narr);
+ ++lfpa;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * Finally, the built in command handlers
+ */
+
+/*
+ * help - tell about commands, or details of a particular command
+ */
+static void
+help(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ struct xcmd *xcp = NULL; /* quiet warning */
+ const char *cmd;
+ const char *list[100];
+ size_t word, words;
+ size_t row, rows;
+ size_t col, cols;
+ size_t length;
+
+ if (pcmd->nargs == 0) {
+ words = 0;
+ for (xcp = builtins; xcp->keyword != NULL; xcp++) {
+ if (*(xcp->keyword) != '?' &&
+ words < COUNTOF(list))
+ list[words++] = xcp->keyword;
+ }
+ for (xcp = opcmds; xcp->keyword != NULL; xcp++)
+ if (words < COUNTOF(list))
+ list[words++] = xcp->keyword;
+
+ qsort((void *)list, words, sizeof(list[0]), helpsort);
+ col = 0;
+ for (word = 0; word < words; word++) {
+ length = strlen(list[word]);
+ col = max(col, length);
+ }
+
+ cols = SCREENWIDTH / ++col;
+ rows = (words + cols - 1) / cols;
+
+ fprintf(fp, "ntpq commands:\n");
+
+ for (row = 0; row < rows; row++) {
+ for (word = row; word < words; word += rows)
+ fprintf(fp, "%-*.*s", (int)col,
+ (int)col - 1, list[word]);
+ fprintf(fp, "\n");
+ }
+ } else {
+ cmd = pcmd->argval[0].string;
+ words = findcmd(cmd, builtins, opcmds, &xcp);
+ if (words == 0) {
+ fprintf(stderr,
+ "Command `%s' is unknown\n", cmd);
+ return;
+ } else if (words >= 2) {
+ fprintf(stderr,
+ "Command `%s' is ambiguous\n", cmd);
+ return;
+ }
+ fprintf(fp, "function: %s\n", xcp->comment);
+ printusage(xcp, fp);
+ }
+}
+
+
+/*
+ * helpsort - do hostname qsort comparisons
+ */
+static int
+helpsort(
+ const void *t1,
+ const void *t2
+ )
+{
+ const char * const * name1 = t1;
+ const char * const * name2 = t2;
+
+ return strcmp(*name1, *name2);
+}
+
+
+/*
+ * printusage - print usage information for a command
+ */
+static void
+printusage(
+ struct xcmd *xcp,
+ FILE *fp
+ )
+{
+ register int i;
+
+ /* XXX: Do we need to warn about extra args here too? */
+
+ (void) fprintf(fp, "usage: %s", xcp->keyword);
+ for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
+ if (xcp->arg[i] & OPT)
+ (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
+ else
+ (void) fprintf(fp, " %s", xcp->desc[i]);
+ }
+ (void) fprintf(fp, "\n");
+}
+
+
+/*
+ * timeout - set time out time
+ */
+static void
+timeout(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ int val;
+
+ if (pcmd->nargs == 0) {
+ val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
+ (void) fprintf(fp, "primary timeout %d ms\n", val);
+ } else {
+ tvout.tv_sec = pcmd->argval[0].uval / 1000;
+ tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
+ * 1000;
+ }
+}
+
+
+/*
+ * auth_delay - set delay for auth requests
+ */
+static void
+auth_delay(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ int isneg;
+ u_long val;
+
+ if (pcmd->nargs == 0) {
+ val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
+ (void) fprintf(fp, "delay %lu ms\n", val);
+ } else {
+ if (pcmd->argval[0].ival < 0) {
+ isneg = 1;
+ val = (u_long)(-pcmd->argval[0].ival);
+ } else {
+ isneg = 0;
+ val = (u_long)pcmd->argval[0].ival;
+ }
+
+ delay_time.l_ui = val / 1000;
+ val %= 1000;
+ delay_time.l_uf = val * 4294967; /* 2**32/1000 */
+
+ if (isneg)
+ L_NEG(&delay_time);
+ }
+}
+
+
+/*
+ * host - set the host we are dealing with.
+ */
+static void
+host(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ int i;
+
+ if (pcmd->nargs == 0) {
+ if (havehost)
+ (void) fprintf(fp, "current host is %s\n",
+ currenthost);
+ else
+ (void) fprintf(fp, "no current host\n");
+ return;
+ }
+
+ i = 0;
+ ai_fam_templ = ai_fam_default;
+ if (pcmd->nargs == 2) {
+ if (!strcmp("-4", pcmd->argval[i].string))
+ ai_fam_templ = AF_INET;
+ else if (!strcmp("-6", pcmd->argval[i].string))
+ ai_fam_templ = AF_INET6;
+ else
+ goto no_change;
+ i = 1;
+ }
+ if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
+ fprintf(fp, "current host set to %s\n", currenthost);
+ } else {
+ no_change:
+ if (havehost)
+ fprintf(fp, "current host remains %s\n",
+ currenthost);
+ else
+ fprintf(fp, "still no current host\n");
+ }
+}
+
+
+/*
+ * poll - do one (or more) polls of the host via NTP
+ */
+/*ARGSUSED*/
+static void
+ntp_poll(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ (void) fprintf(fp, "poll not implemented yet\n");
+}
+
+
+/*
+ * showdrefid2str - return a string explanation of the value of drefid
+ */
+static const char *
+showdrefid2str(void)
+{
+ switch (drefid) {
+ case REFID_HASH:
+ return "hash";
+ case REFID_IPV4:
+ return "ipv4";
+ default:
+ return "Unknown";
+ }
+}
+
+
+/*
+ * drefid - display/change "display hash"
+ */
+static void
+showdrefid(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (pcmd->nargs == 0) {
+ (void) fprintf(fp, "drefid value is %s\n", showdrefid2str());
+ return;
+ } else if (STREQ(pcmd->argval[0].string, "hash")) {
+ drefid = REFID_HASH;
+ } else if (STREQ(pcmd->argval[0].string, "ipv4")) {
+ drefid = REFID_IPV4;
+ } else {
+ (void) fprintf(fp, "What?\n");
+ return;
+ }
+ (void) fprintf(fp, "drefid value set to %s\n", showdrefid2str());
+}
+
+
+/*
+ * keyid - get a keyid to use for authenticating requests
+ */
+static void
+keyid(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (pcmd->nargs == 0) {
+ if (info_auth_keyid == 0)
+ (void) fprintf(fp, "no keyid defined\n");
+ else
+ (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
+ } else {
+ /* allow zero so that keyid can be cleared. */
+ if(pcmd->argval[0].uval > NTP_MAXKEY)
+ (void) fprintf(fp, "Invalid key identifier\n");
+ info_auth_keyid = pcmd->argval[0].uval;
+ }
+}
+
+/*
+ * keytype - get type of key to use for authenticating requests
+ */
+static void
+keytype(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ const char * digest_name;
+ size_t digest_len;
+ int key_type;
+
+ if (!pcmd->nargs) {
+ fprintf(fp, "keytype is %s with %lu octet digests\n",
+ keytype_name(info_auth_keytype),
+ (u_long)info_auth_hashlen);
+ return;
+ }
+
+ digest_name = pcmd->argval[0].string;
+ digest_len = 0;
+ key_type = keytype_from_text(digest_name, &digest_len);
+
+ if (!key_type) {
+ fprintf(fp, "keytype is not valid. "
+#ifdef OPENSSL
+ "Type \"help keytype\" for the available digest types.\n");
+#else
+ "Only \"md5\" is available.\n");
+#endif
+ return;
+ }
+
+ info_auth_keytype = key_type;
+ info_auth_hashlen = digest_len;
+}
+
+
+/*
+ * passwd - get an authentication key
+ */
+/*ARGSUSED*/
+static void
+passwd(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ const char *pass;
+
+ if (info_auth_keyid == 0) {
+ info_auth_keyid = getkeyid("Keyid: ");
+ if (info_auth_keyid == 0) {
+ (void)fprintf(fp, "Keyid must be defined\n");
+ return;
+ }
+ }
+ if (pcmd->nargs >= 1)
+ pass = pcmd->argval[0].string;
+ else {
+ pass = getpass_keytype(info_auth_keytype);
+ if ('\0' == pass[0]) {
+ fprintf(fp, "Password unchanged\n");
+ return;
+ }
+ }
+ authusekey(info_auth_keyid, info_auth_keytype,
+ (const u_char *)pass);
+ authtrust(info_auth_keyid, 1);
+}
+
+
+/*
+ * hostnames - set the showhostnames flag
+ */
+static void
+hostnames(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (pcmd->nargs == 0) {
+ if (showhostnames)
+ (void) fprintf(fp, "hostnames being shown\n");
+ else
+ (void) fprintf(fp, "hostnames not being shown\n");
+ } else {
+ if (STREQ(pcmd->argval[0].string, "yes"))
+ showhostnames = 1;
+ else if (STREQ(pcmd->argval[0].string, "no"))
+ showhostnames = 0;
+ else
+ (void)fprintf(stderr, "What?\n");
+ }
+}
+
+
+
+/*
+ * setdebug - set/change debugging level
+ */
+static void
+setdebug(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (pcmd->nargs == 0) {
+ (void) fprintf(fp, "debug level is %d\n", debug);
+ return;
+ } else if (STREQ(pcmd->argval[0].string, "no")) {
+ debug = 0;
+ } else if (STREQ(pcmd->argval[0].string, "more")) {
+ debug++;
+ } else if (STREQ(pcmd->argval[0].string, "less")) {
+ debug--;
+ } else {
+ (void) fprintf(fp, "What?\n");
+ return;
+ }
+ (void) fprintf(fp, "debug level set to %d\n", debug);
+}
+
+
+/*
+ * quit - stop this nonsense
+ */
+/*ARGSUSED*/
+static void
+quit(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (havehost)
+ closesocket(sockfd); /* cleanliness next to godliness */
+ exit(0);
+}
+
+
+/*
+ * version - print the current version number
+ */
+/*ARGSUSED*/
+static void
+version(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+
+ (void) fprintf(fp, "%s\n", Version);
+ return;
+}
+
+
+/*
+ * raw - set raw mode output
+ */
+/*ARGSUSED*/
+static void
+raw(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ rawmode = 1;
+ (void) fprintf(fp, "Output set to raw\n");
+}
+
+
+/*
+ * cooked - set cooked mode output
+ */
+/*ARGSUSED*/
+static void
+cooked(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ rawmode = 0;
+ (void) fprintf(fp, "Output set to cooked\n");
+ return;
+}
+
+
+/*
+ * authenticate - always authenticate requests to this host
+ */
+static void
+authenticate(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (pcmd->nargs == 0) {
+ if (always_auth) {
+ (void) fprintf(fp,
+ "authenticated requests being sent\n");
+ } else
+ (void) fprintf(fp,
+ "unauthenticated requests being sent\n");
+ } else {
+ if (STREQ(pcmd->argval[0].string, "yes")) {
+ always_auth = 1;
+ } else if (STREQ(pcmd->argval[0].string, "no")) {
+ always_auth = 0;
+ } else
+ (void)fprintf(stderr, "What?\n");
+ }
+}
+
+
+/*
+ * ntpversion - choose the NTP version to use
+ */
+static void
+ntpversion(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ if (pcmd->nargs == 0) {
+ (void) fprintf(fp,
+ "NTP version being claimed is %d\n", pktversion);
+ } else {
+ if (pcmd->argval[0].uval < NTP_OLDVERSION
+ || pcmd->argval[0].uval > NTP_VERSION) {
+ (void) fprintf(stderr, "versions %d to %d, please\n",
+ NTP_OLDVERSION, NTP_VERSION);
+ } else {
+ pktversion = (u_char) pcmd->argval[0].uval;
+ }
+ }
+}
+
+
+static void __attribute__((__format__(__printf__, 1, 0)))
+vwarning(const char *fmt, va_list ap)
+{
+ int serrno = errno;
+ (void) fprintf(stderr, "%s: ", progname);
+ vfprintf(stderr, fmt, ap);
+ (void) fprintf(stderr, ": %s\n", strerror(serrno));
+}
+
+/*
+ * warning - print a warning message
+ */
+static void __attribute__((__format__(__printf__, 1, 2)))
+warning(
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vwarning(fmt, ap);
+ va_end(ap);
+}
+
+
+/*
+ * error - print a message and exit
+ */
+static void __attribute__((__format__(__printf__, 1, 2)))
+error(
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vwarning(fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+/*
+ * getkeyid - prompt the user for a keyid to use
+ */
+static u_long
+getkeyid(
+ const char *keyprompt
+ )
+{
+ int c;
+ FILE *fi;
+ char pbuf[20];
+ size_t i;
+ size_t ilim;
+
+#ifndef SYS_WINNT
+ if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
+#else
+ if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
+#endif /* SYS_WINNT */
+ fi = stdin;
+ else
+ setbuf(fi, (char *)NULL);
+ fprintf(stderr, "%s", keyprompt); fflush(stderr);
+ for (i = 0, ilim = COUNTOF(pbuf) - 1;
+ i < ilim && (c = getc(fi)) != '\n' && c != EOF;
+ )
+ pbuf[i++] = (char)c;
+ pbuf[i] = '\0';
+ if (fi != stdin)
+ fclose(fi);
+
+ return (u_long) atoi(pbuf);
+}
+
+
+/*
+ * atoascii - printable-ize possibly ascii data using the character
+ * transformations cat -v uses.
+ */
+static void
+atoascii(
+ const char *in,
+ size_t in_octets,
+ char *out,
+ size_t out_octets
+ )
+{
+ const u_char * pchIn;
+ const u_char * pchInLimit;
+ u_char * pchOut;
+ u_char c;
+
+ pchIn = (const u_char *)in;
+ pchInLimit = pchIn + in_octets;
+ pchOut = (u_char *)out;
+
+ if (NULL == pchIn) {
+ if (0 < out_octets)
+ *pchOut = '\0';
+ return;
+ }
+
+#define ONEOUT(c) \
+do { \
+ if (0 == --out_octets) { \
+ *pchOut = '\0'; \
+ return; \
+ } \
+ *pchOut++ = (c); \
+} while (0)
+
+ for ( ; pchIn < pchInLimit; pchIn++) {
+ c = *pchIn;
+ if ('\0' == c)
+ break;
+ if (c & 0x80) {
+ ONEOUT('M');
+ ONEOUT('-');
+ c &= 0x7f;
+ }
+ if (c < ' ') {
+ ONEOUT('^');
+ ONEOUT((u_char)(c + '@'));
+ } else if (0x7f == c) {
+ ONEOUT('^');
+ ONEOUT('?');
+ } else
+ ONEOUT(c);
+ }
+ ONEOUT('\0');
+
+#undef ONEOUT
+}
+
+
+/*
+ * makeascii - print possibly ascii data using the character
+ * transformations that cat -v uses.
+ */
+void
+makeascii(
+ size_t length,
+ const char *data,
+ FILE *fp
+ )
+{
+ const u_char *data_u_char;
+ const u_char *cp;
+ int c;
+
+ data_u_char = (const u_char *)data;
+
+ for (cp = data_u_char; cp < data_u_char + length; cp++) {
+ c = (int)*cp;
+ if (c & 0x80) {
+ putc('M', fp);
+ putc('-', fp);
+ c &= 0x7f;
+ }
+
+ if (c < ' ') {
+ putc('^', fp);
+ putc(c + '@', fp);
+ } else if (0x7f == c) {
+ putc('^', fp);
+ putc('?', fp);
+ } else
+ putc(c, fp);
+ }
+}
+
+
+/*
+ * asciize - same thing as makeascii except add a newline
+ */
+void
+asciize(
+ int length,
+ char *data,
+ FILE *fp
+ )
+{
+ makeascii(length, data, fp);
+ putc('\n', fp);
+}
+
+
+/*
+ * truncate string to fit clipping excess at end.
+ * "too long" -> "too l"
+ * Used for hostnames.
+ */
+const char *
+trunc_right(
+ const char * src,
+ size_t width
+ )
+{
+ size_t sl;
+ char * out;
+
+
+ sl = strlen(src);
+ if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
+ LIB_GETBUF(out);
+ memcpy(out, src, width);
+ out[width] = '\0';
+
+ return out;
+ }
+
+ return src;
+}
+
+
+/*
+ * truncate string to fit by preserving right side and using '_' to hint
+ * "too long" -> "_long"
+ * Used for local IPv6 addresses, where low bits differentiate.
+ */
+const char *
+trunc_left(
+ const char * src,
+ size_t width
+ )
+{
+ size_t sl;
+ char * out;
+
+
+ sl = strlen(src);
+ if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
+ LIB_GETBUF(out);
+ out[0] = '_';
+ memcpy(&out[1], &src[sl + 1 - width], width);
+
+ return out;
+ }
+
+ return src;
+}
+
+
+/*
+ * Some circular buffer space
+ */
+#define CBLEN 80
+#define NUMCB 6
+
+char circ_buf[NUMCB][CBLEN];
+int nextcb = 0;
+
+/* --------------------------------------------------------------------
+ * Parsing a response value list
+ *
+ * This sounds simple (and it actually is not really hard) but it has
+ * some pitfalls.
+ *
+ * Rule1: CR/LF is never embedded in an item
+ * Rule2: An item is a name, optionally followed by a value
+ * Rule3: The value is separated from the name by a '='
+ * Rule4: Items are separated by a ','
+ * Rule5: values can be quoted by '"', in which case they can contain
+ * arbitrary characters but *not* '"', CR and LF
+ *
+ * There are a few implementations out there that require a somewhat
+ * relaxed attitude when parsing a value list, especially since we want
+ * to copy names and values into local buffers. If these would overflow,
+ * the item should be skipped without terminating the parsing sequence.
+ *
+ * Also, for empty values, there might be a '=' after the name or not;
+ * we treat that equivalent.
+ *
+ * Parsing an item definitely breaks on a CR/LF. If an item is not
+ * followed by a comma (','), parsing stops. In the middle of a quoted
+ * character sequence CR/LF terminates the parsing finally without
+ * returning a value.
+ *
+ * White space and other noise is ignored when parsing the data buffer;
+ * only CR, LF, ',', '=' and '"' are characters with a special meaning.
+ * White space is stripped from the names and values *after* working
+ * through the buffer, before making the local copies. If whitespace
+ * stripping results in an empty name, parsing resumes.
+ */
+
+/*
+ * nextvar parsing helpers
+ */
+
+/* predicate: allowed chars inside a quoted string */
+static int/*BOOL*/ cp_qschar(int ch)
+{
+ return ch && (ch != '"' && ch != '\r' && ch != '\n');
+}
+
+/* predicate: allowed chars inside an unquoted string */
+static int/*BOOL*/ cp_uqchar(int ch)
+{
+ return ch && (ch != ',' && ch != '"' && ch != '\r' && ch != '\n');
+}
+
+/* predicate: allowed chars inside a value name */
+static int/*BOOL*/ cp_namechar(int ch)
+{
+ return ch && (ch != ',' && ch != '=' && ch != '\r' && ch != '\n');
+}
+
+/* predicate: characters *between* list items. We're relaxed here. */
+static int/*BOOL*/ cp_ivspace(int ch)
+{
+ return (ch == ',' || (ch > 0 && ch <= ' '));
+}
+
+/* get current character (or NUL when on end) */
+static inline int
+pf_getch(
+ const char ** datap,
+ const char * endp
+ )
+{
+ return (*datap != endp)
+ ? *(const unsigned char*)*datap
+ : '\0';
+}
+
+/* get next character (or NUL when on end) */
+static inline int
+pf_nextch(
+ const char ** datap,
+ const char * endp
+ )
+{
+ return (*datap != endp && ++(*datap) != endp)
+ ? *(const unsigned char*)*datap
+ : '\0';
+}
+
+static size_t
+str_strip(
+ const char ** datap,
+ size_t len
+ )
+{
+ static const char empty[] = "";
+
+ if (*datap && len) {
+ const char * cpl = *datap;
+ const char * cpr = cpl + len;
+
+ while (cpl != cpr && *(const unsigned char*)cpl <= ' ')
+ ++cpl;
+ while (cpl != cpr && *(const unsigned char*)(cpr - 1) <= ' ')
+ --cpr;
+ *datap = cpl;
+ len = (size_t)(cpr - cpl);
+ } else {
+ *datap = empty;
+ len = 0;
+ }
+ return len;
+}
+
+static void
+pf_error(
+ const char * what,
+ const char * where,
+ const char * whend
+ )
+{
+# ifndef BUILD_AS_LIB
+
+ FILE * ofp = (debug > 0) ? stdout : stderr;
+ size_t len = (size_t)(whend - where);
+
+ if (len > 50) /* *must* fit into an 'int'! */
+ len = 50;
+ fprintf(ofp, "nextvar: %s: '%.*s'\n",
+ what, (int)len, where);
+
+# else /*defined(BUILD_AS_LIB)*/
+
+ UNUSED_ARG(what);
+ UNUSED_ARG(where);
+ UNUSED_ARG(whend);
+
+# endif /*defined(BUILD_AS_LIB)*/
+}
+
+/*
+ * nextvar - find the next variable in the buffer
+ */
+int/*BOOL*/
+nextvar(
+ size_t *datalen,
+ const char **datap,
+ char **vname,
+ char **vvalue
+ )
+{
+ enum PState { sDone, sInit, sName, sValU, sValQ };
+
+ static char name[MAXVARLEN], value[MAXVALLEN];
+
+ const char *cp, *cpend;
+ const char *np, *vp;
+ size_t nlen, vlen;
+ int ch;
+ enum PState st;
+
+ cpend = *datap + *datalen;
+
+ again:
+ np = vp = NULL;
+ nlen = vlen = 0;
+
+ st = sInit;
+ ch = pf_getch(datap, cpend);
+
+ while (st != sDone) {
+ switch (st)
+ {
+ case sInit: /* handle inter-item chars */
+ while (cp_ivspace(ch))
+ ch = pf_nextch(datap, cpend);
+ if (cp_namechar(ch)) {
+ np = *datap;
+ cp = np;
+ st = sName;
+ ch = pf_nextch(datap, cpend);
+ } else {
+ goto final_done;
+ }
+ break;
+
+ case sName: /* collect name */
+ while (cp_namechar(ch))
+ ch = pf_nextch(datap, cpend);
+ nlen = (size_t)(*datap - np);
+ if (ch == '=') {
+ ch = pf_nextch(datap, cpend);
+ vp = *datap;
+ st = sValU;
+ } else {
+ if (ch != ',')
+ *datap = cpend;
+ st = sDone;
+ }
+ break;
+
+ case sValU: /* collect unquoted part(s) of value */
+ while (cp_uqchar(ch))
+ ch = pf_nextch(datap, cpend);
+ if (ch == '"') {
+ ch = pf_nextch(datap, cpend);
+ st = sValQ;
+ } else {
+ vlen = (size_t)(*datap - vp);
+ if (ch != ',')
+ *datap = cpend;
+ st = sDone;
+ }
+ break;
+
+ case sValQ: /* collect quoted part(s) of value */
+ while (cp_qschar(ch))
+ ch = pf_nextch(datap, cpend);
+ if (ch == '"') {
+ ch = pf_nextch(datap, cpend);
+ st = sValU;
+ } else {
+ pf_error("no closing quote, stop", cp, cpend);
+ goto final_done;
+ }
+ break;
+
+ default:
+ pf_error("state machine error, stop", *datap, cpend);
+ goto final_done;
+ }
+ }
+
+ /* If name or value do not fit their buffer, croak and start
+ * over. If there's no name at all after whitespace stripping,
+ * redo silently.
+ */
+ nlen = str_strip(&np, nlen);
+ vlen = str_strip(&vp, vlen);
+
+ if (nlen == 0) {
+ goto again;
+ }
+ if (nlen >= sizeof(name)) {
+ pf_error("runaway name", np, cpend);
+ goto again;
+ }
+ if (vlen >= sizeof(value)) {
+ pf_error("runaway value", vp, cpend);
+ goto again;
+ }
+
+ /* copy name and value into NUL-terminated buffers */
+ memcpy(name, np, nlen);
+ name[nlen] = '\0';
+ *vname = name;
+
+ memcpy(value, vp, vlen);
+ value[vlen] = '\0';
+ *vvalue = value;
+
+ /* check if there's more to do or if we are finshed */
+ *datalen = (size_t)(cpend - *datap);
+ return TRUE;
+
+ final_done:
+ *datap = cpend;
+ *datalen = 0;
+ return FALSE;
+}
+
+
+u_short
+varfmt(const char * varname)
+{
+ u_int n;
+
+ for (n = 0; n < COUNTOF(cookedvars); n++)
+ if (!strcmp(varname, cookedvars[n].varname))
+ return cookedvars[n].fmt;
+
+ return PADDING;
+}
+
+
+/*
+ * printvars - print variables returned in response packet
+ */
+void
+printvars(
+ size_t length,
+ const char *data,
+ int status,
+ int sttype,
+ int quiet,
+ FILE *fp
+ )
+{
+ if (rawmode)
+ rawprint(sttype, length, data, status, quiet, fp);
+ else
+ cookedprint(sttype, length, data, status, quiet, fp);
+}
+
+
+/*
+ * rawprint - do a printout of the data in raw mode
+ */
+static void
+rawprint(
+ int datatype,
+ size_t length,
+ const char *data,
+ int status,
+ int quiet,
+ FILE *fp
+ )
+{
+ const char *cp;
+ const char *cpend;
+
+ /*
+ * Essentially print the data as is. We reformat unprintables, though.
+ */
+ cp = data;
+ cpend = data + length;
+
+ if (!quiet)
+ (void) fprintf(fp, "status=0x%04x,\n", status);
+
+ while (cp < cpend) {
+ if (*cp == '\r') {
+ /*
+ * If this is a \r and the next character is a
+ * \n, supress this, else pretty print it. Otherwise
+ * just output the character.
+ */
+ if (cp == (cpend - 1) || *(cp + 1) != '\n')
+ makeascii(1, cp, fp);
+ } else if (isspace(pgetc(cp)) || isprint(pgetc(cp)))
+ putc(*cp, fp);
+ else
+ makeascii(1, cp, fp);
+ cp++;
+ }
+}
+
+
+/*
+ * Global data used by the cooked output routines
+ */
+int out_chars; /* number of characters output */
+int out_linecount; /* number of characters output on this line */
+
+
+/*
+ * startoutput - get ready to do cooked output
+ */
+static void
+startoutput(void)
+{
+ out_chars = 0;
+ out_linecount = 0;
+}
+
+
+/*
+ * output - output a variable=value combination
+ */
+static void
+output(
+ FILE *fp,
+ const char *name,
+ const char *value
+ )
+{
+ int len;
+
+ /* strlen of "name=value" */
+ len = size2int_sat(strlen(name) + 1 + strlen(value));
+
+ if (out_chars != 0) {
+ out_chars += 2;
+ if ((out_linecount + len + 2) > MAXOUTLINE) {
+ fputs(",\n", fp);
+ out_linecount = 0;
+ } else {
+ fputs(", ", fp);
+ out_linecount += 2;
+ }
+ }
+
+ fputs(name, fp);
+ putc('=', fp);
+ fputs(value, fp);
+ out_chars += len;
+ out_linecount += len;
+}
+
+
+/*
+ * endoutput - terminate a block of cooked output
+ */
+static void
+endoutput(
+ FILE *fp
+ )
+{
+ if (out_chars != 0)
+ putc('\n', fp);
+}
+
+
+/*
+ * outputarr - output an array of values
+ */
+static void
+outputarr(
+ FILE *fp,
+ char *name,
+ int narr,
+ l_fp *lfp,
+ int issigned
+ )
+{
+ char *bp;
+ char *cp;
+ size_t i;
+ size_t len;
+ char buf[256];
+
+ bp = buf;
+ /*
+ * Hack to align delay and offset values
+ */
+ for (i = (int)strlen(name); i < 11; i++)
+ *bp++ = ' ';
+
+ for (i = narr; i > 0; i--) {
+ if (i != (size_t)narr)
+ *bp++ = ' ';
+ cp = (issigned ? lfptoms(lfp, 2) : ulfptoms(lfp, 2));
+ len = strlen(cp);
+ if (len > 7) {
+ cp[7] = '\0';
+ len = 7;
+ }
+ while (len < 7) {
+ *bp++ = ' ';
+ len++;
+ }
+ while (*cp != '\0')
+ *bp++ = *cp++;
+ lfp++;
+ }
+ *bp = '\0';
+ output(fp, name, buf);
+}
+
+static char *
+tstflags(
+ u_long val
+ )
+{
+# if CBLEN < 10
+# error BLEN is too small -- increase!
+# endif
+
+ char *cp, *s;
+ size_t cb, i;
+ int l;
+
+ s = cp = circ_buf[nextcb];
+ if (++nextcb >= NUMCB)
+ nextcb = 0;
+ cb = sizeof(circ_buf[0]);
+
+ l = snprintf(cp, cb, "%02lx", val);
+ if (l < 0 || (size_t)l >= cb)
+ goto fail;
+ cp += l;
+ cb -= l;
+ if (!val) {
+ l = strlcat(cp, " ok", cb);
+ if ((size_t)l >= cb)
+ goto fail;
+ cp += l;
+ cb -= l;
+ } else {
+ const char *sep;
+
+ sep = " ";
+ for (i = 0; i < COUNTOF(tstflagnames); i++) {
+ if (val & 0x1) {
+ l = snprintf(cp, cb, "%s%s", sep,
+ tstflagnames[i]);
+ if (l < 0)
+ goto fail;
+ if ((size_t)l >= cb) {
+ cp += cb - 4;
+ cb = 4;
+ l = strlcpy (cp, "...", cb);
+ cp += l;
+ cb -= l;
+ break;
+ }
+ sep = ", ";
+ cp += l;
+ cb -= l;
+ }
+ val >>= 1;
+ }
+ }
+
+ return s;
+
+ fail:
+ *cp = '\0';
+ return s;
+}
+
+/*
+ * cookedprint - output variables in cooked mode
+ */
+static void
+cookedprint(
+ int datatype,
+ size_t length,
+ const char *data,
+ int status,
+ int quiet,
+ FILE *fp
+ )
+{
+ char *name;
+ char *value;
+ char output_raw;
+ int fmt;
+ l_fp lfp;
+ sockaddr_u hval;
+ u_long uval;
+ int narr;
+ size_t len;
+ l_fp lfparr[8];
+ char b[12];
+ char bn[2 * MAXVARLEN];
+ char bv[2 * MAXVALLEN];
+
+ UNUSED_ARG(datatype);
+
+ if (!quiet)
+ fprintf(fp, "status=%04x %s,\n", status,
+ statustoa(datatype, status));
+
+ startoutput();
+ while (nextvar(&length, &data, &name, &value)) {
+ fmt = varfmt(name);
+ output_raw = 0;
+ switch (fmt) {
+
+ case PADDING:
+ output_raw = '*';
+ break;
+
+ case TS:
+ if (!value || !decodets(value, &lfp))
+ output_raw = '?';
+ else
+ output(fp, name, prettydate(&lfp));
+ break;
+
+ case HA: /* fallthru */
+ case NA:
+ if (!value || !decodenetnum(value, &hval)) {
+ output_raw = '?';
+ } else if (fmt == HA){
+ output(fp, name, nntohost(&hval));
+ } else {
+ output(fp, name, stoa(&hval));
+ }
+ break;
+
+ case RF:
+ if (!value) {
+ output_raw = '?';
+ } else if (decodenetnum(value, &hval)) {
+ if (datatype == TYPE_CLOCK && IS_IPV4(&hval)) {
+ /*
+ * Workaround to override numeric refid formats
+ * for refclocks received from faulty nptd servers
+ * and output them as text.
+ */
+ int i;
+ unsigned char *str = (unsigned char *)&(hval.sa4).sin_addr;
+ char refid_buf[5];
+ for (i=0; i<4 && str[i]; i++)
+ refid_buf[i] = (isprint(str[i]) ? str[i] : '?');
+ refid_buf[i] = 0; /* Null terminator */
+ output(fp, name, refid_buf);
+ } else if (ISREFCLOCKADR(&hval)) {
+ output(fp, name, refnumtoa(&hval));
+ } else {
+ if (drefid == REFID_IPV4) {
+ output(fp, name, stoa(&hval));
+ } else {
+ char refid_buf[12];
+ snprintf (refid_buf, sizeof(refid_buf),
+ "0x%08x", ntohl(addr2refid(&hval)));
+ output(fp, name, refid_buf);
+ }
+ }
+ } else if (strlen(value) <= 4) {
+ output(fp, name, value);
+ } else {
+ output_raw = '?';
+ }
+ break;
+
+ case LP:
+ if (!value || !decodeuint(value, &uval) || uval > 3) {
+ output_raw = '?';
+ } else {
+ b[0] = (0x2 & uval)
+ ? '1'
+ : '0';
+ b[1] = (0x1 & uval)
+ ? '1'
+ : '0';
+ b[2] = '\0';
+ output(fp, name, b);
+ }
+ break;
+
+ case OC:
+ if (!value || !decodeuint(value, &uval)) {
+ output_raw = '?';
+ } else {
+ snprintf(b, sizeof(b), "%03lo", uval);
+ output(fp, name, b);
+ }
+ break;
+
+ case AU:
+ case AS:
+ if (!value || !decodearr(value, &narr, lfparr, 8))
+ output_raw = '?';
+ else
+ outputarr(fp, name, narr, lfparr, (fmt==AS));
+ break;
+
+ case FX:
+ if (!value || !decodeuint(value, &uval))
+ output_raw = '?';
+ else
+ output(fp, name, tstflags(uval));
+ break;
+
+ case SN:
+ if (!value)
+ output_raw = '?';
+ else if (isdigit(*value)) { /* number without sign */
+ bv[0] = '+';
+ atoascii (value, MAXVALLEN, bv+1, sizeof(bv)-1);
+ output(fp, name, bv);
+ } else
+ output_raw = '*'; /* output as-is */
+ break;
+
+ default:
+ fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
+ name, value, fmt);
+ output_raw = '?';
+ break;
+ }
+
+ if (output_raw != 0) {
+ /* TALOS-CAN-0063: avoid buffer overrun */
+ atoascii(name, MAXVARLEN, bn, sizeof(bn));
+ if (output_raw != '*') {
+ atoascii(value, MAXVALLEN,
+ bv, sizeof(bv) - 1);
+ len = strlen(bv);
+ bv[len] = output_raw;
+ bv[len+1] = '\0';
+ } else {
+ atoascii(value, MAXVALLEN,
+ bv, sizeof(bv));
+ }
+ output(fp, bn, bv);
+ }
+ }
+ endoutput(fp);
+}
+
+
+/*
+ * sortassoc - sort associations in the cache into ascending order
+ */
+void
+sortassoc(void)
+{
+ if (numassoc > 1)
+ qsort(assoc_cache, (size_t)numassoc,
+ sizeof(assoc_cache[0]), &assoccmp);
+}
+
+
+/*
+ * assoccmp - compare two associations
+ */
+static int
+assoccmp(
+ const void *t1,
+ const void *t2
+ )
+{
+ const struct association *ass1 = t1;
+ const struct association *ass2 = t2;
+
+ if (ass1->assid < ass2->assid)
+ return -1;
+ if (ass1->assid > ass2->assid)
+ return 1;
+ return 0;
+}
+
+
+/*
+ * grow_assoc_cache() - enlarge dynamic assoc_cache array
+ *
+ * The strategy is to add an assumed 4k page size at a time, leaving
+ * room for malloc() bookkeeping overhead equivalent to 4 pointers.
+ */
+void
+grow_assoc_cache(void)
+{
+ static size_t prior_sz;
+ size_t new_sz;
+
+ new_sz = prior_sz + 4 * 1024;
+ if (0 == prior_sz) {
+ new_sz -= 4 * sizeof(void *);
+ }
+ assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
+ prior_sz = new_sz;
+ assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
+}
+
+
+/*
+ * ntpq_custom_opt_handler - autoopts handler for -c and -p
+ *
+ * By default, autoopts loses the relative order of -c and -p options
+ * on the command line. This routine replaces the default handler for
+ * those routines and builds a list of commands to execute preserving
+ * the order.
+ */
+void
+ntpq_custom_opt_handler(
+ tOptions *pOptions,
+ tOptDesc *pOptDesc
+ )
+{
+ switch (pOptDesc->optValue) {
+
+ default:
+ fprintf(stderr,
+ "ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
+ pOptDesc->optValue, pOptDesc->optValue);
+ exit(1);
+
+ case 'c':
+ ADDCMD(pOptDesc->pzLastArg);
+ break;
+
+ case 'p':
+ ADDCMD("peers");
+ break;
+ }
+}
+/*
+ * Obtain list of digest names
+ */
+
+#if defined(OPENSSL) && !defined(HAVE_EVP_MD_DO_ALL_SORTED)
+# if defined(_MSC_VER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
+# define HAVE_EVP_MD_DO_ALL_SORTED
+# endif
+#endif
+
+#ifdef OPENSSL
+# ifdef HAVE_EVP_MD_DO_ALL_SORTED
+# define K_PER_LINE 8
+# define K_NL_PFX_STR "\n "
+# define K_DELIM_STR ", "
+
+struct hstate {
+ char *list;
+ const char **seen;
+ int idx;
+};
+
+
+# ifndef BUILD_AS_LIB
+static void
+list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg)
+{
+ size_t len, n;
+ const char *name, **seen;
+ struct hstate *hstate = arg;
+ const char *cp;
+
+ /* m is MD obj, from is name or alias, to is base name for alias */
+ if (!m || !from || to)
+ return; /* Ignore aliases */
+
+ /* Discard MACs that NTP won't accept. */
+ /* Keep this consistent with keytype_from_text() in ssl_init.c. */
+ if (EVP_MD_size(m) > (MAX_MAC_LEN - sizeof(keyid_t)))
+ return;
+
+ name = EVP_MD_name(m);
+
+ /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
+
+ for (cp = name; *cp; cp++)
+ if (islower((unsigned char)*cp))
+ return;
+
+ len = (cp - name) + 1;
+
+ /* There are duplicates. Discard if name has been seen. */
+
+ for (seen = hstate->seen; *seen; seen++)
+ if (!strcmp(*seen, name))
+ return;
+
+ n = (seen - hstate->seen) + 2;
+ hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
+ hstate->seen[n-2] = name;
+ hstate->seen[n-1] = NULL;
+
+ if (hstate->list != NULL)
+ len += strlen(hstate->list);
+
+ len += (hstate->idx >= K_PER_LINE)
+ ? strlen(K_NL_PFX_STR)
+ : strlen(K_DELIM_STR);
+
+ if (hstate->list == NULL) {
+ hstate->list = (char *)emalloc(len);
+ hstate->list[0] = '\0';
+ } else {
+ hstate->list = (char *)erealloc(hstate->list, len);
+ }
+
+ sprintf(hstate->list + strlen(hstate->list), "%s%s",
+ ((hstate->idx >= K_PER_LINE) ? K_NL_PFX_STR : K_DELIM_STR),
+ name);
+
+ if (hstate->idx >= K_PER_LINE)
+ hstate->idx = 1;
+ else
+ hstate->idx++;
+}
+# endif /* !defined(BUILD_AS_LIB) */
+
+# ifndef BUILD_AS_LIB
+/* Insert CMAC into SSL digests list */
+static char *
+insert_cmac(char *list)
+{
+#ifdef ENABLE_CMAC
+ int insert;
+ size_t len;
+
+
+ /* If list empty, we need to insert CMAC on new line */
+ insert = (!list || !*list);
+
+ if (insert) {
+ len = strlen(K_NL_PFX_STR) + strlen(CMAC);
+ list = (char *)erealloc(list, len + 1);
+ sprintf(list, "%s%s", K_NL_PFX_STR, CMAC);
+ } else { /* List not empty */
+ /* Check if CMAC already in list - future proofing */
+ const char *cmac_sn;
+ char *cmac_p;
+
+ cmac_sn = OBJ_nid2sn(NID_cmac);
+ cmac_p = list;
+ insert = cmac_sn != NULL && *cmac_sn != '\0';
+
+ /* CMAC in list if found, followed by nul char or ',' */
+ while (insert && NULL != (cmac_p = strstr(cmac_p, cmac_sn))) {
+ cmac_p += strlen(cmac_sn);
+ /* Still need to insert if not nul and not ',' */
+ insert = *cmac_p && ',' != *cmac_p;
+ }
+
+ /* Find proper insertion point */
+ if (insert) {
+ char *last_nl;
+ char *point;
+ char *delim;
+ int found;
+
+ /* Default to start if list empty */
+ found = 0;
+ delim = list;
+ len = strlen(list);
+
+ /* While new lines */
+ while (delim < list + len && *delim &&
+ !strncmp(K_NL_PFX_STR, delim, strlen(K_NL_PFX_STR))) {
+ point = delim + strlen(K_NL_PFX_STR);
+
+ /* While digest names on line */
+ while (point < list + len && *point) {
+ /* Another digest after on same or next line? */
+ delim = strstr( point, K_DELIM_STR);
+ last_nl = strstr( point, K_NL_PFX_STR);
+
+ /* No - end of list */
+ if (!delim && !last_nl) {
+ delim = list + len;
+ } else
+ /* New line and no delim or before delim? */
+ if (last_nl && (!delim || last_nl < delim)) {
+ delim = last_nl;
+ }
+
+ /* Found insertion point where CMAC before entry? */
+ if (strncmp(CMAC, point, delim - point) < 0) {
+ found = 1;
+ break;
+ }
+
+ if (delim < list + len && *delim &&
+ !strncmp(K_DELIM_STR, delim, strlen(K_DELIM_STR))) {
+ point += strlen(K_DELIM_STR);
+ } else {
+ break;
+ }
+ } /* While digest names on line */
+ } /* While new lines */
+
+ /* If found in list */
+ if (found) {
+ /* insert cmac and delim */
+ /* Space for list could move - save offset */
+ ptrdiff_t p_offset = point - list;
+ len += strlen(CMAC) + strlen(K_DELIM_STR);
+ list = (char *)erealloc(list, len + 1);
+ point = list + p_offset;
+ /* move to handle src/dest overlap */
+ memmove(point + strlen(CMAC) + strlen(K_DELIM_STR),
+ point, strlen(point) + 1);
+ strncpy(point, CMAC, strlen(CMAC));
+ strncpy(point + strlen(CMAC), K_DELIM_STR, strlen(K_DELIM_STR));
+ } else { /* End of list */
+ /* append delim and cmac */
+ len += strlen(K_DELIM_STR) + strlen(CMAC);
+ list = (char *)erealloc(list, len + 1);
+ strcpy(list + strlen(list), K_DELIM_STR);
+ strcpy(list + strlen(list), CMAC);
+ }
+ } /* insert */
+ } /* List not empty */
+#endif /*ENABLE_CMAC*/
+ return list;
+}
+# endif /* !defined(BUILD_AS_LIB) */
+# endif
+#endif
+
+
+#ifndef BUILD_AS_LIB
+static char *
+list_digest_names(void)
+{
+ char *list = NULL;
+
+#ifdef OPENSSL
+# ifdef HAVE_EVP_MD_DO_ALL_SORTED
+ struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
+
+ /* replace calloc(1, sizeof(const char *)) */
+ hstate.seen = (const char **)emalloc_zero(sizeof(const char *));
+
+ INIT_SSL();
+ EVP_MD_do_all_sorted(list_md_fn, &hstate);
+ list = hstate.list;
+ free(hstate.seen);
+
+ list = insert_cmac(list); /* Insert CMAC into SSL digests list */
+
+# else
+ list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
+ strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
+# endif
+#else
+ list = (char *)emalloc(sizeof("md5"));
+ strcpy(list, "md5");
+#endif
+
+ return list;
+}
+#endif /* !defined(BUILD_AS_LIB) */
+
+#define CTRLC_STACK_MAX 4
+static volatile size_t ctrlc_stack_len = 0;
+static volatile Ctrl_C_Handler ctrlc_stack[CTRLC_STACK_MAX];
+
+
+
+int/*BOOL*/
+push_ctrl_c_handler(
+ Ctrl_C_Handler func
+ )
+{
+ size_t size = ctrlc_stack_len;
+ if (func && (size < CTRLC_STACK_MAX)) {
+ ctrlc_stack[size] = func;
+ ctrlc_stack_len = size + 1;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int/*BOOL*/
+pop_ctrl_c_handler(
+ Ctrl_C_Handler func
+ )
+{
+ size_t size = ctrlc_stack_len;
+ if (size) {
+ --size;
+ if (func == NULL || func == ctrlc_stack[size]) {
+ ctrlc_stack_len = size;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+#ifndef BUILD_AS_LIB
+static void
+on_ctrlc(void)
+{
+ size_t size = ctrlc_stack_len;
+ while (size)
+ if ((*ctrlc_stack[--size])())
+ break;
+}
+#endif /* !defined(BUILD_AS_LIB) */
+
+#ifndef BUILD_AS_LIB
+static int
+my_easprintf(
+ char ** ppinto,
+ const char * fmt ,
+ ...
+ )
+{
+ va_list va;
+ int prc;
+ size_t len = 128;
+ char * buf = emalloc(len);
+
+ again:
+ /* Note: we expect the memory allocation to fail long before the
+ * increment in buffer size actually overflows.
+ */
+ buf = (buf) ? erealloc(buf, len) : emalloc(len);
+
+ va_start(va, fmt);
+ prc = vsnprintf(buf, len, fmt, va);
+ va_end(va);
+
+ if (prc < 0) {
+ /* might be very old vsnprintf. Or actually MSVC... */
+ len += len >> 1;
+ goto again;
+ }
+ if ((size_t)prc >= len) {
+ /* at least we have the proper size now... */
+ len = (size_t)prc + 1;
+ goto again;
+ }
+ if ((size_t)prc < (len - 32))
+ buf = erealloc(buf, (size_t)prc + 1);
+ *ppinto = buf;
+ return prc;
+}
+#endif /* !defined(BUILD_AS_LIB) */
diff --git a/bsd/freebsd/contrib/ntp/ntpq/ntpq.h b/bsd/freebsd/contrib/ntp/ntpq/ntpq.h
new file mode 100644
index 0000000..53f7638
--- /dev/null
+++ b/bsd/freebsd/contrib/ntp/ntpq/ntpq.h
@@ -0,0 +1,162 @@
+/*
+ * ntpq.h - definitions of interest to ntpq
+ */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_stdlib.h"
+#include "ntp_string.h"
+#include "ntp_malloc.h"
+#include "ntp_assert.h"
+#include "ntp_control.h"
+#include "lib_strbuf.h"
+
+#include "ntpq-opts.h"
+
+/*
+ * Maximum number of arguments
+ */
+#define MAXARGS 4
+
+/*
+ * Limit on packets in a single response. Increasing this value to
+ * 96 will marginally speed "mrulist" operation on lossless networks
+ * but it has been observed to cause loss on WiFi networks and with
+ * an IPv6 go6.net tunnel over UDP. That loss causes the request
+ * row limit to be cut in half, and it grows back very slowly to
+ * ensure forward progress is made and loss isn't triggered too quickly
+ * afterward. While the lossless case gains only marginally with
+ * MAXFRAGS == 96, the lossy case is a lot slower due to the repeated
+ * timeouts. Empirally, MAXFRAGS == 32 avoids most of the routine loss
+ * on both the WiFi and UDP v6 tunnel tests and seems a good compromise.
+ * This suggests some device in the path has a limit of 32 ~512 byte UDP
+ * packets in queue.
+ * Lowering MAXFRAGS may help with particularly lossy networks, but some
+ * ntpq commands may rely on the longtime value of 24 implicitly,
+ * assuming a single multipacket response will be large enough for any
+ * needs. In contrast, the "mrulist" command is implemented as a series
+ * of requests and multipacket responses to each.
+ */
+#define MAXFRAGS 32
+
+/*
+ * Error codes for internal use
+ */
+#define ERR_UNSPEC 256
+#define ERR_INCOMPLETE 257
+#define ERR_TIMEOUT 258
+#define ERR_TOOMUCH 259
+
+/*
+ * Flags for forming descriptors.
+ */
+#define OPT 0x80 /* this argument is optional, or'd with type */
+
+#define NO 0x0
+#define NTP_STR 0x1 /* string argument */
+#define NTP_UINT 0x2 /* unsigned integer */
+#define NTP_INT 0x3 /* signed integer */
+#define NTP_ADD 0x4 /* IP network address */
+#define IP_VERSION 0x5 /* IP version */
+#define NTP_ADP 0x6 /* IP address and port */
+#define NTP_LFP 0x7 /* NTP timestamp */
+#define NTP_MODE 0x8 /* peer mode */
+#define NTP_2BIT 0x9 /* leap bits */
+#define NTP_REFID 0xA /* RefID */
+
+/*
+ * Arguments are returned in a union
+ */
+typedef union {
+ const char *string;
+ long ival;
+ u_long uval;
+ sockaddr_u netnum;
+} arg_v;
+
+/*
+ * Structure for passing parsed command line
+ */
+struct parse {
+ const char *keyword;
+ arg_v argval[MAXARGS];
+ size_t nargs;
+};
+
+/*
+ * ntpdc includes a command parser which could charitably be called
+ * crude. The following structure is used to define the command
+ * syntax.
+ */
+struct xcmd {
+ const char *keyword; /* command key word */
+ void (*handler) (struct parse *, FILE *); /* command handler */
+ u_char arg[MAXARGS]; /* descriptors for arguments */
+ const char *desc[MAXARGS]; /* descriptions for arguments */
+ const char *comment;
+};
+
+/*
+ * Structure to hold association data
+ */
+struct association {
+ associd_t assid;
+ u_short status;
+};
+
+/*
+ * mrulist terminal status interval
+ */
+#define MRU_REPORT_SECS 5
+
+/*
+ * var_format is used to override cooked formatting for selected vars.
+ */
+typedef struct var_format_tag {
+ const char * varname;
+ u_short fmt;
+} var_format;
+
+typedef struct chost_tag chost;
+struct chost_tag {
+ const char *name;
+ int fam;
+};
+
+extern chost chosts[];
+
+extern int interactive; /* are we prompting? */
+extern int old_rv; /* use old rv behavior? --old-rv */
+extern te_Refid drefid; /* How should we display a refid? */
+extern u_int assoc_cache_slots;/* count of allocated array entries */
+extern u_int numassoc; /* number of cached associations */
+extern u_int numhosts;
+
+extern void grow_assoc_cache(void);
+extern void asciize (int, char *, FILE *);
+extern int getnetnum (const char *, sockaddr_u *, char *, int);
+extern void sortassoc (void);
+extern void show_error_msg (int, associd_t);
+extern int dogetassoc (FILE *);
+extern int doquery (int, associd_t, int, size_t, const char *,
+ u_short *, size_t *, const char **);
+extern int doqueryex (int, associd_t, int, size_t, const char *,
+ u_short *, size_t *, const char **, int);
+extern const char * nntohost (sockaddr_u *);
+extern const char * nntohost_col (sockaddr_u *, size_t, int);
+extern const char * nntohostp (sockaddr_u *);
+extern int decodets (char *, l_fp *);
+extern int decodeuint (char *, u_long *);
+extern int nextvar (size_t *, const char **, char **, char **);
+extern int decodetime (char *, l_fp *);
+extern void printvars (size_t, const char *, int, int, int, FILE *);
+extern int decodeint (char *, long *);
+extern void makeascii (size_t, const char *, FILE *);
+extern const char * trunc_left (const char *, size_t);
+extern const char * trunc_right(const char *, size_t);
+
+typedef int/*BOOL*/ (*Ctrl_C_Handler)(void);
+extern int/*BOOL*/ push_ctrl_c_handler(Ctrl_C_Handler);
+extern int/*BOOL*/ pop_ctrl_c_handler(Ctrl_C_Handler);