diff options
author | Chris Johns <chrisj@rtems.org> | 2023-04-23 19:04:13 -1000 |
---|---|---|
committer | KinseyMoore <48726349+KinseyMoore@users.noreply.github.com> | 2023-05-15 21:45:54 -0500 |
commit | 3f071ffe05fc14bf41c2075882fdb5493c4a2e5b (patch) | |
tree | 91c6a66c77630b7efcbd2c6aef99bcf9a1b2ddef | |
parent | bsd/ntp: Use msyslog support for %m (diff) | |
download | rtems-net-services-3f071ffe05fc14bf41c2075882fdb5493c4a2e5b.tar.bz2 |
bsd/ntp: Import ntpq
-rwxr-xr-x | bsd/freebsd/contrib/ntp/include/ntp_lineedit.h | 13 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/libntp/atolfp.c | 122 | ||||
-rwxr-xr-x | bsd/freebsd/contrib/ntp/libntp/atouint.c | 42 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/libntp/authusekey.c | 34 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/libntp/caltontp.c | 68 | ||||
-rwxr-xr-x | bsd/freebsd/contrib/ntp/libntp/hextoint.c | 42 | ||||
-rwxr-xr-x | bsd/freebsd/contrib/ntp/libntp/hextolfp.c | 70 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/libntp/mstolfp.c | 99 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/libntp/octtoint.c | 36 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/libntp/socktohost.c | 118 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/ntpq/libntpq.c | 772 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/ntpq/libntpq.h | 109 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/ntpq/libntpq_subs.c | 52 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.c | 1220 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/ntpq/ntpq-opts.h | 307 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/ntpq/ntpq-subs.c | 4142 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/ntpq/ntpq.c | 4174 | ||||
-rw-r--r-- | bsd/freebsd/contrib/ntp/ntpq/ntpq.h | 162 |
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); |