#include <config.h>
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <setjmp.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"
#include "libssl_compat.h"
#endif
#include <ssl_applink.c>
#include "ntp_libopts.h"
#include "safecast.h"
#ifdef SYS_VXWORKS
# define open(name, flags) open(name, flags, 0777)
# define SERVER_PORT_NUM 123
#endif
#ifdef COMMAND
# undef COMMAND
#endif
int interactive = 0;
const char *prompt = "ntpq> ";
int old_rv = 1;
te_Refid drefid = -1;
s_char sys_precision;
u_long info_auth_keyid = 0;
static int info_auth_keytype = NID_md5;
static size_t info_auth_hashlen = 16;
u_long current_time;
int always_auth = 0;
int rawmode = 0;
u_char pktversion = NTP_OLDVERSION + 1;
volatile int jump = 0;
#define PADDING 0
#define HA 1
#define NA 2
#define LP 3
#define RF 4
#define AR 5
#define FX 6
#define TS 7
#define OC 8
#define EOV 255
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 },
{ "dstadr", NA },
{ "filtdelay", AR },
{ "filtoffset", AR },
{ "filtdisp", AR },
{ "filterror", AR },
};
static const char *tstflagnames[] = {
"pkt_dup",
"pkt_bogus",
"pkt_unsync",
"pkt_denied",
"pkt_auth",
"pkt_stratum",
"pkt_header",
"pkt_autokey",
"pkt_crypto",
"peer_stratum",
"peer_dist",
"peer_loop",
"peer_unreach"
};
int ntpqmain (int, char **);
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
static void docmd (const char *);
static void tokenize (const char *, char **, int *);
static int getarg (const char *, int, arg_v *);
#endif
static int findcmd (const char *, struct xcmd *,
struct xcmd *, struct xcmd **);
static int rtdatetolfp (char *, l_fp *);
static int decodearr (char *, int *, l_fp *);
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 *, ...)
__attribute__((__format__(__printf__, 1, 2)));
static void error (const char *, ...)
__attribute__((__format__(__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 *);
static int assoccmp (const void *, const void *);
static void on_ctrlc (void);
u_short varfmt (const char *);
static int my_easprintf (char**, const char *, ...) NTP_PRINTF(2, 3);
void ntpq_custom_opt_handler (tOptions *, tOptDesc *);
#ifdef OPENSSL
# ifdef HAVE_EVP_MD_DO_ALL_SORTED
static void list_md_fn(const EVP_MD *m, const char *from,
const char *to, void *arg );
# endif
#endif
static char *list_digest_names(void);
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 },
{ "", "", "", "" }, "" }
};
#define DEFHOST "localhost"
#define DEFTIMEOUT 5
#define DEFSTIMEOUT 3
#define DEFDELAY 0x51EB852
#define LENHOSTNAME 256
#define MAXCMDS 100
#define MAXHOSTS 200
#define MAXLINE 512
#define MAXTOKENS (1+MAXARGS+2)
#define MAXVARLEN 256
#define MAXVALLEN 2048
#define MAXOUTLINE 72
#define SCREENWIDTH 76
struct sock_timeval tvout = { DEFTIMEOUT, 0 };
struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };
l_fp delay_time;
char currenthost[LENHOSTNAME];
int currenthostisnum;
struct sockaddr_in hostaddr;
int showhostnames = 1;
int wideremote = 0;
int ai_fam_templ;
int ai_fam_default;
SOCKET sockfd;
int havehost = 0;
int s_port = 0;
struct servent *server_entry = NULL;
u_short sequence;
#define DATASIZE (MAXFRAGS*480)
long pktdata[DATASIZE/sizeof(long)];
struct association * assoc_cache;
u_int assoc_cache_slots;
u_int numassoc;
size_t numcmds = 0;
const char *ccmds[MAXCMDS];
#define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
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)
#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)
jmp_buf interrupt_buf;
FILE *current_output;
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;
ntp_optind = 0;
server_entry = NULL;
havehost = 0;
numassoc = 0;
numcmds = 0;
numhosts = 0;
}
#endif
#endif
#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();
ssl_applink();
init_auth();
if (!ipv6_works)
ai_fam_default = AF_INET;
{
char *list;
char *msg;
list = list_digest_names();
for (icmd = 0; icmd < sizeof(builtins)/sizeof(builtins[0]); icmd++) {
if (strcmp("keytype", builtins[icmd].keyword) == 0)
break;
}
INSIST(icmd < sizeof(builtins)/sizeof(builtins[0]));
#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;
}
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 ('4' == argv[ihost][1]) {
ai_fam_templ = AF_INET;
continue;
} else if ('6' == argv[ihost][1]) {
ai_fam_templ = AF_INET6;
continue;
} else {
}
}
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
if (interactive)
push_ctrl_c_handler(abortcmd);
#endif
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))
for (icmd = 0; icmd < numcmds; icmd++)
docmd(ccmds[icmd]);
}
}
#ifdef SYS_WINNT
WSACleanup();
#endif
return 0;
}
#endif
static int
openhost(
const char *hname,
int fam
)
{
const char svc[] = "ntp";
char temphost[LENHOSTNAME];
int a_info, i;
struct addrinfo hints, *ai;
sockaddr_u addr;
size_t octets;
register const char *cp;
char name[LENHOSTNAME];
cp = hname;
if (*cp == '[') {
cp++;
for (i = 0; *cp && *cp != ']'; cp++, i++)
name[i] = *cp;
if (*cp == ']') {
name[i] = '\0';
hname = name;
} else {
return 0;
}
}
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
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));
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
#ifdef SYS_WINNT
{
int optionValue = SO_SYNCHRONOUS_NONALERT;
int err;
err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
(char *)&optionValue, sizeof(optionValue));
if (err) {
mfprintf(stderr,
"setsockopt(SO_SYNCHRONOUS_NONALERT)"
" error: %m\n");
freeaddrinfo(ai);
exit(1);
}
}
#endif
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;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
&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
{
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
)
{
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);
}
}
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;
}
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] = {0,};
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;
uint32_t tobase;
uint32_t tospan;
uint32_t todiff;
memset(offsets, 0, sizeof(offsets));
memset(counts , 0, sizeof(counts ));
*rsize = 0;
if (rstatus)
*rstatus = 0;
*rdata = (char *)pktdata;
numfrags = 0;
seenlastfrag = 0;
tobase = (uint32_t)time(NULL);
FD_ZERO(&fds);
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)
if (errno == EINTR) {
seenlastfrag = 1;
goto maybe_final;
}
#endif
warning("select fails");
return -1;
}
todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
if ((n > 0) && (todiff > tospan)) {
n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
n -= n;
}
if (n <= 0) {
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);
}
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;
}
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;
}
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;
}
if (ntohs(rpkt.associd) != associd) {
TRACE(1, ("Association ID %d doesn't match expected %d\n",
ntohs(rpkt.associd), associd));
#ifdef notdef
continue;
#endif
}
offset = ntohs(rpkt.offset);
count = ntohs(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;
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;
}
TRACE(2, ("Packet okay\n"));
if (numfrags > (MAXFRAGS - 1)) {
TRACE(2, ("Number of fragments exceeds maximum %d\n",
MAXFRAGS - 1));
return ERR_TOOMUCH;
}
for (f = 0;
f < numfrags && offsets[f] < offset;
f++) {
;
}
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++;
if (!CTL_ISMORE(rpkt.r_m_e_op)) {
seenlastfrag = 1;
if (rstatus != 0)
*rstatus = ntohs(rpkt.status);
}
memcpy((char *)pktdata + offset, &rpkt.u, count);
tobase = (uint32_t)time(NULL);
#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;
}
}
}
}
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;
if (qsize > CTL_MAX_DATA_LEN) {
fprintf(stderr,
"***Internal error! qsize (%zu) too large\n",
qsize);
return 1;
}
memset(&qpkt, 0, sizeof(qpkt));
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 (qsize > 0) {
memcpy(&qpkt.u, qdata, (size_t)qsize);
pktsize += qsize;
while (pktsize & (sizeof(u_int32) - 1)) {
qpkt.u.data[qsize++] = 0;
pktsize++;
}
}
if (!auth && !always_auth) {
return sendpkt(&qpkt, pktsize);
}
while (pktsize & 7) {
qpkt.u.data[qsize++] = 0;
pktsize++;
}
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);
}
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);
}
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);
}
}
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);
}
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;
if (!havehost) {
fprintf(stderr, "***No host open, use `host' command\n");
return -1;
}
done = 0;
sequence++;
again:
res = sendrequest(opcode, associd, auth, qsize, qdata);
if (res != 0)
return res;
res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
if (res > 0) {
if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
if (res == ERR_INCOMPLETE) {
sequence++;
}
done = 1;
goto again;
}
if (!quiet)
show_error_msg(res, associd);
}
return res;
}
#ifndef BUILD_AS_LIB
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
#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
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
#ifndef BUILD_AS_LIB
static void
docmd(
const char *cmdline
)
{
char *tokens[1+MAXARGS+2];
struct parse pcmd;
int ntok;
static int i;
struct xcmd *xcmd;
tokenize(cmdline, tokens, &ntok);
if (ntok == 0)
return;
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;
}
for (i = MAXARGS + 1; i < ntok ; ++i) {
fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
}
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;
}
i = 1;
} else {
current_output = stdout;
i = 0;
}
if (interactive && setjmp(interrupt_buf)) {
jump = 0;
return;
} else {
jump++;
(xcmd->handler)(&pcmd, current_output);
jump = 0;
if (i) (void) fclose(current_output);
}
return;
}
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;
while (ISSPACE(*cp))
cp++;
if (ISEOL(*cp))
break;
if (*ntok == 1 && tokens[0][0] == ':') {
do {
if (sp - tspace >= MAXLINE)
goto toobig;
*sp++ = *cp++;
} while (!ISEOL(*cp));
}
else if (*cp == '\"') {
++cp;
do {
if (sp - tspace >= MAXLINE)
goto toobig;
*sp++ = *cp++;
} while ((*cp != '\"') && !ISEOL(*cp));
}
else {
do {
if (sp - tspace >= MAXLINE)
goto toobig;
*sp++ = *cp++;
} while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
}
if (sp - tspace >= MAXLINE)
goto toobig;
*sp++ = '\0';
}
return;
toobig:
*ntok = 0;
fprintf(stderr,
"***Line `%s' is too big\n",
line);
return;
}
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
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++) {
if (*str != *(cl->keyword))
continue;
if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
if (*((cl->keyword) + clen) == '\0') {
*cmd = cl;
return 1;
}
nmatch++;
nearmatch = cl;
}
}
if (clist == clist1 && clist2 != 0) {
clist = clist2;
goto again;
}
if (nmatch == 1) {
*cmd = nearmatch;
return 1;
}
return nmatch;
}
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
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;
}
const char *
nntohost(
sockaddr_u *netnum
)
{
return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
}
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;
}
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;
}
static int
rtdatetolfp(
char *str,
l_fp *lfp
)
{
register char *cp;
register int i;
struct calendar cal;
char buf[4];
cal.yearday = 0;
cp = str;
if (!isdigit((int)*cp)) {
if (*cp == '-') {
L_CLR(lfp);
return 1;
}
return 0;
}
cal.monthday = (u_char) (*cp++ - '0');
if (isdigit((int)*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((int)*cp))
return 0;
cal.year = (u_short)(*cp++ - '0');
if (isdigit((int)*cp)) {
cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
cal.year = (u_short)(*cp++ - '0');
}
if (isdigit((int)*cp)) {
cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
cal.year = (u_short)(cal.year + *cp++ - '0');
}
if (isdigit((int)*cp)) {
cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
cal.year = (u_short)(cal.year + *cp++ - '0');
}
if (cal.year == 0) {
L_CLR(lfp);
return 1;
}
if (*cp++ != ' ' || !isdigit((int)*cp))
return 0;
cal.hour = (u_char)(*cp++ - '0');
if (isdigit((int)*cp)) {
cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
cal.hour = (u_char)(cal.hour + *cp++ - '0');
}
if (*cp++ != ':' || !isdigit((int)*cp))
return 0;
cal.minute = (u_char)(*cp++ - '0');
if (isdigit((int)*cp)) {
cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
cal.minute = (u_char)(cal.minute + *cp++ - '0');
}
if (*cp++ != ':' || !isdigit((int)*cp))
return 0;
cal.second = (u_char)(*cp++ - '0');
if (isdigit((int)*cp)) {
cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
cal.second = (u_char)(cal.second + *cp++ - '0');
}
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;
}
int
decodets(
char *str,
l_fp *lfp
)
{
char *cp;
char buf[30];
size_t b;
if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
return hextolfp(str+2, lfp);
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);
}
if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
return hextolfp(str, lfp);
if (atolfp(str, lfp))
return 1;
return rtdatetolfp(str, lfp);
}
int
decodetime(
char *str,
l_fp *lfp
)
{
return mstolfp(str, lfp);
}
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);
}
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));
}
static int
decodearr(
char *str,
int *narr,
l_fp *lfparr
)
{
register char *cp, *bp;
register l_fp *lfp;
char buf[60];
lfp = lfparr;
cp = str;
*narr = 0;
while (*narr < 8) {
while (isspace((int)*cp))
cp++;
if (*cp == '\0')
break;
bp = buf;
while (!isspace((int)*cp) && *cp != '\0')
*bp++ = *cp++;
*bp++ = '\0';
if (!decodetime(buf, lfp))
return 0;
(*narr)++;
lfp++;
}
return 1;
}
static void
help(
struct parse *pcmd,
FILE *fp
)
{
struct xcmd *xcp = NULL;
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);
}
}
static int
helpsort(
const void *t1,
const void *t2
)
{
const char * const * name1 = t1;
const char * const * name2 = t2;
return strcmp(*name1, *name2);
}
static void
printusage(
struct xcmd *xcp,
FILE *fp
)
{
register int i;
(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");
}
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;
}
}
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;
if (isneg)
L_NEG(&delay_time);
}
}
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");
}
}
static void
ntp_poll(
struct parse *pcmd,
FILE *fp
)
{
(void) fprintf(fp, "poll not implemented yet\n");
}
static char *
showdrefid2str(void)
{
switch (drefid) {
case REFID_HASH:
return "hash";
case REFID_IPV4:
return "ipv4";
default:
return "Unknown";
}
}
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());
}
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 {
if(pcmd->argval[0].uval > NTP_MAXKEY)
(void) fprintf(fp, "Invalid key identifier\n");
info_auth_keyid = pcmd->argval[0].uval;
}
}
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;
}
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);
}
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");
}
}
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);
}
static void
quit(
struct parse *pcmd,
FILE *fp
)
{
if (havehost)
closesocket(sockfd);
exit(0);
}
static void
version(
struct parse *pcmd,
FILE *fp
)
{
(void) fprintf(fp, "%s\n", Version);
return;
}
static void
raw(
struct parse *pcmd,
FILE *fp
)
{
rawmode = 1;
(void) fprintf(fp, "Output set to raw\n");
}
static void
cooked(
struct parse *pcmd,
FILE *fp
)
{
rawmode = 0;
(void) fprintf(fp, "Output set to cooked\n");
return;
}
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");
}
}
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));
}
static void __attribute__((__format__(__printf__, 1, 2)))
warning(
const char *fmt,
...
)
{
va_list ap;
va_start(ap, fmt);
vwarning(fmt, ap);
va_end(ap);
}
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);
}
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
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);
}
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
}
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);
}
}
void
asciize(
int length,
char *data,
FILE *fp
)
{
makeascii(length, data, fp);
putc('\n', fp);
}
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;
}
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;
}
#define CBLEN 80
#define NUMCB 6
char circ_buf[NUMCB][CBLEN];
int nextcb = 0;
int
nextvar(
size_t *datalen,
const char **datap,
char **vname,
char **vvalue
)
{
const char *cp;
const char *np;
const char *cpend;
size_t srclen;
size_t len;
static char name[MAXVARLEN];
static char value[MAXVALLEN];
cp = *datap;
cpend = cp + *datalen;
while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
cp++;
if (cp >= cpend)
return 0;
srclen = strcspn(cp, ",=\r\n");
srclen = min(srclen, (size_t)(cpend - cp));
len = srclen;
while (len > 0 && isspace((unsigned char)cp[len - 1]))
len--;
if (len >= sizeof(name))
return 0;
if (len > 0)
memcpy(name, cp, len);
name[len] = '\0';
*vname = name;
cp += srclen;
if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
if (cp < cpend)
cp++;
*datap = cp;
*datalen = size2int_sat(cpend - cp);
*vvalue = NULL;
return 1;
}
cp++;
while (cp < cpend && (isspace((unsigned char)*cp) && *cp != '\r' && *cp != '\n'))
cp++;
np = cp;
if ('"' == *np) {
do {
np++;
} while (np < cpend && '"' != *np);
if (np < cpend && '"' == *np)
np++;
} else {
while (np < cpend && ',' != *np && '\r' != *np)
np++;
}
len = np - cp;
if (np > cpend || len >= sizeof(value) ||
(np < cpend && ',' != *np && '\r' != *np))
return 0;
memcpy(value, cp, len);
while (len > 0 && isspace((unsigned char)value[len - 1]))
len--;
value[len] = '\0';
if (np < cpend && ',' == *np)
np++;
*datap = np;
*datalen = size2int_sat(cpend - np);
*vvalue = value;
return 1;
}
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;
}
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);
}
static void
rawprint(
int datatype,
size_t length,
const char *data,
int status,
int quiet,
FILE *fp
)
{
const char *cp;
const char *cpend;
cp = data;
cpend = data + length;
if (!quiet)
(void) fprintf(fp, "status=0x%04x,\n", status);
while (cp < cpend) {
if (*cp == '\r') {
if (cp == (cpend - 1) || *(cp + 1) != '\n')
makeascii(1, cp, fp);
} else if (isspace((unsigned char)*cp) || isprint((unsigned char)*cp))
putc(*cp, fp);
else
makeascii(1, cp, fp);
cp++;
}
}
int out_chars;
int out_linecount;
static void
startoutput(void)
{
out_chars = 0;
out_linecount = 0;
}
static void
output(
FILE *fp,
const char *name,
const char *value
)
{
int len;
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;
}
static void
endoutput(
FILE *fp
)
{
if (out_chars != 0)
putc('\n', fp);
}
static void
outputarr(
FILE *fp,
char *name,
int narr,
l_fp *lfp
)
{
char *bp;
char *cp;
size_t i;
size_t len;
char buf[256];
bp = buf;
for (i = (int)strlen(name); i < 11; i++)
*bp++ = ' ';
for (i = narr; i > 0; i--) {
if (i != narr)
*bp++ = ' ';
cp = lfptoms(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
)
{
register char *cp, *s;
size_t cb;
register int i;
register const char *sep;
sep = "";
s = cp = circ_buf[nextcb];
if (++nextcb >= NUMCB)
nextcb = 0;
cb = sizeof(circ_buf[0]);
snprintf(cp, cb, "%02lx", val);
cp += strlen(cp);
cb -= strlen(cp);
if (!val) {
strlcat(cp, " ok", cb);
cp += strlen(cp);
cb -= strlen(cp);
} else {
if (cb) {
*cp++ = ' ';
cb--;
}
for (i = 0; i < (int)COUNTOF(tstflagnames); i++) {
if (val & 0x1) {
snprintf(cp, cb, "%s%s", sep,
tstflagnames[i]);
sep = ", ";
cp += strlen(cp);
cb -= strlen(cp);
}
val >>= 1;
}
}
if (cb)
*cp = '\0';
return s;
}
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 (!decodets(value, &lfp))
output_raw = '?';
else
output(fp, name, prettydate(&lfp));
break;
case HA:
case NA:
if (!decodenetnum(value, &hval)) {
output_raw = '?';
} else if (fmt == HA){
output(fp, name, nntohost(&hval));
} else {
output(fp, name, stoa(&hval));
}
break;
case RF:
if (decodenetnum(value, &hval)) {
if (ISREFCLOCKADR(&hval))
output(fp, name,
refnumtoa(&hval));
else
output(fp, name, stoa(&hval));
} else if (strlen(value) <= 4) {
output(fp, name, value);
} else {
output_raw = '?';
}
break;
case LP:
if (!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 (!decodeuint(value, &uval)) {
output_raw = '?';
} else {
snprintf(b, sizeof(b), "%03lo", uval);
output(fp, name, b);
}
break;
case AR:
if (!decodearr(value, &narr, lfparr))
output_raw = '?';
else
outputarr(fp, name, narr, lfparr);
break;
case FX:
if (!decodeuint(value, &uval))
output_raw = '?';
else
output(fp, name, tstflags(uval));
break;
default:
fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
name, value, fmt);
output_raw = '?';
break;
}
if (output_raw != 0) {
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);
}
void
sortassoc(void)
{
if (numassoc > 1)
qsort(assoc_cache, (size_t)numassoc,
sizeof(assoc_cache[0]), &assoccmp);
}
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;
}
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]));
}
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;
}
}
#ifdef OPENSSL
# ifdef HAVE_EVP_MD_DO_ALL_SORTED
struct hstate {
char *list;
const char **seen;
int idx;
};
#define K_PER_LINE 8
#define K_NL_PFX_STR "\n "
#define K_DELIM_STR ", "
static void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg )
{
size_t len, n;
const char *name, *cp, **seen;
struct hstate *hstate = arg;
EVP_MD_CTX *ctx;
u_int digest_len;
u_char digest[EVP_MAX_MD_SIZE];
if (!m)
return;
name = EVP_MD_name(m);
for( cp = name; *cp; cp++ ) {
if( islower((unsigned char)*cp) )
return;
}
len = (cp - name) + 1;
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;
ctx = EVP_MD_CTX_new();
EVP_DigestInit(ctx, EVP_get_digestbyname(name));
EVP_DigestFinal(ctx, digest, &digest_len);
EVP_MD_CTX_free(ctx);
if (digest_len > (MAX_MAC_LEN - sizeof(keyid_t)))
return;
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
#endif
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 };
hstate.seen = (const char **) emalloc_zero(1*sizeof( const char * ));
INIT_SSL();
EVP_MD_do_all_sorted(list_md_fn, &hstate);
list = hstate.list;
free(hstate.seen);
# 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;
}
#define CTRLC_STACK_MAX 4
static volatile size_t ctrlc_stack_len = 0;
static volatile Ctrl_C_Handler ctrlc_stack[CTRLC_STACK_MAX];
int
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
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;
}
static void
on_ctrlc(void)
{
size_t size = ctrlc_stack_len;
while (size)
if ((*ctrlc_stack[--size])())
break;
}
static int
my_easprintf(
char ** ppinto,
const char * fmt ,
...
)
{
va_list va;
int prc;
size_t len = 128;
char * buf = emalloc(len);
again:
buf = (buf) ? erealloc(buf, len) : emalloc(len);
va_start(va, fmt);
prc = vsnprintf(buf, len, fmt, va);
va_end(va);
if (prc < 0) {
len += len >> 1;
goto again;
}
if ((size_t)prc >= len) {
len = (size_t)prc + 1;
goto again;
}
if ((size_t)prc < (len - 32))
buf = erealloc(buf, (size_t)prc + 1);
*ppinto = buf;
return prc;
}