#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_refclock.h"
#include "ntp_control.h"
#include "ntp_unixtime.h"
#include "ntp_stdlib.h"
#include "ntp_config.h"
#include "ntp_assert.h"
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/stat.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <arpa/inet.h>
struct ctl_proc {
short control_code;
#define NO_REQUEST (-1)
u_short flags;
#define NOAUTH 0
#define AUTH 1
void (*handler) (struct recvbuf *, int);
};
static void ctl_error (int);
#ifdef REFCLOCK
static u_short ctlclkstatus (struct refclockstat *);
#endif
static void ctl_flushpkt (int);
static void ctl_putdata (const char *, unsigned int, int);
static void ctl_putstr (const char *, const char *,
unsigned int);
static void ctl_putdbl (const char *, double);
static void ctl_putuint (const char *, u_long);
static void ctl_puthex (const char *, u_long);
static void ctl_putint (const char *, long);
static void ctl_putts (const char *, l_fp *);
static void ctl_putadr (const char *, u_int32,
sockaddr_u *);
static void ctl_putid (const char *, char *);
static void ctl_putarray (const char *, double *, int);
static void ctl_putsys (int);
static void ctl_putpeer (int, struct peer *);
static void ctl_putfs (const char *, tstamp_t);
#ifdef REFCLOCK
static void ctl_putclock (int, struct refclockstat *, int);
#endif
static struct ctl_var *ctl_getitem (struct ctl_var *, char **);
static u_long count_var (struct ctl_var *);
static void control_unspec (struct recvbuf *, int);
static void read_status (struct recvbuf *, int);
static void read_variables (struct recvbuf *, int);
static void write_variables (struct recvbuf *, int);
static void read_clock_status (struct recvbuf *, int);
static void write_clock_status (struct recvbuf *, int);
static void set_trap (struct recvbuf *, int);
static void unset_trap (struct recvbuf *, int);
static void configure (struct recvbuf *, int);
static void save_config (struct recvbuf *, int);
static struct ctl_trap *ctlfindtrap (sockaddr_u *,
struct interface *);
static struct ctl_proc control_codes[] = {
{ CTL_OP_UNSPEC, NOAUTH, control_unspec },
{ CTL_OP_READSTAT, NOAUTH, read_status },
{ CTL_OP_READVAR, NOAUTH, read_variables },
{ CTL_OP_WRITEVAR, AUTH, write_variables },
{ CTL_OP_READCLOCK, NOAUTH, read_clock_status },
{ CTL_OP_WRITECLOCK, NOAUTH, write_clock_status },
{ CTL_OP_SETTRAP, NOAUTH, set_trap },
{ CTL_OP_UNSETTRAP, NOAUTH, unset_trap },
{ CTL_OP_SAVECONFIG, AUTH, save_config },
{ CTL_OP_CONFIGURE, AUTH, configure },
{ NO_REQUEST, 0 }
};
static struct ctl_var sys_var[] = {
{ 0, PADDING, "" },
{ CS_LEAP, RW, "leap" },
{ CS_STRATUM, RO, "stratum" },
{ CS_PRECISION, RO, "precision" },
{ CS_ROOTDELAY, RO, "rootdelay" },
{ CS_ROOTDISPERSION, RO, "rootdisp" },
{ CS_REFID, RO, "refid" },
{ CS_REFTIME, RO, "reftime" },
{ CS_POLL, RO, "tc" },
{ CS_PEERID, RO, "peer" },
{ CS_OFFSET, RO, "offset" },
{ CS_DRIFT, RO, "frequency" },
{ CS_JITTER, RO, "sys_jitter" },
{ CS_ERROR, RO, "clk_jitter" },
{ CS_CLOCK, RO, "clock" },
{ CS_PROCESSOR, RO, "processor" },
{ CS_SYSTEM, RO, "system" },
{ CS_VERSION, RO, "version" },
{ CS_STABIL, RO, "clk_wander" },
{ CS_VARLIST, RO, "sys_var_list" },
{ CS_TAI, RO, "tai" },
{ CS_LEAPTAB, RO, "leapsec" },
{ CS_LEAPEND, RO, "expire" },
{ CS_RATE, RO, "mintc" },
#ifdef OPENSSL
{ CS_FLAGS, RO, "flags" },
{ CS_HOST, RO, "host" },
{ CS_PUBLIC, RO, "update" },
{ CS_CERTIF, RO, "cert" },
{ CS_SIGNATURE, RO, "signature" },
{ CS_REVTIME, RO, "until" },
{ CS_GROUP, RO, "group" },
{ CS_DIGEST, RO, "digest" },
#endif
{ 0, EOV, "" }
};
static struct ctl_var *ext_sys_var = (struct ctl_var *)0;
static u_char def_sys_var[] = {
CS_VERSION,
CS_PROCESSOR,
CS_SYSTEM,
CS_LEAP,
CS_STRATUM,
CS_PRECISION,
CS_ROOTDELAY,
CS_ROOTDISPERSION,
CS_REFID,
CS_REFTIME,
CS_CLOCK,
CS_PEERID,
CS_POLL,
CS_RATE,
CS_OFFSET,
CS_DRIFT,
CS_JITTER,
CS_ERROR,
CS_STABIL,
CS_TAI,
CS_LEAPTAB,
CS_LEAPEND,
#ifdef OPENSSL
CS_HOST,
CS_GROUP,
CS_FLAGS,
CS_DIGEST,
CS_SIGNATURE,
CS_PUBLIC,
CS_CERTIF,
#endif
0
};
static struct ctl_var peer_var[] = {
{ 0, PADDING, "" },
{ CP_CONFIG, RO, "config" },
{ CP_AUTHENABLE, RO, "authenable" },
{ CP_AUTHENTIC, RO, "authentic" },
{ CP_SRCADR, RO, "srcadr" },
{ CP_SRCPORT, RO, "srcport" },
{ CP_DSTADR, RO, "dstadr" },
{ CP_DSTPORT, RO, "dstport" },
{ CP_LEAP, RO, "leap" },
{ CP_HMODE, RO, "hmode" },
{ CP_STRATUM, RO, "stratum" },
{ CP_PPOLL, RO, "ppoll" },
{ CP_HPOLL, RO, "hpoll" },
{ CP_PRECISION, RO, "precision" },
{ CP_ROOTDELAY, RO, "rootdelay" },
{ CP_ROOTDISPERSION, RO, "rootdisp" },
{ CP_REFID, RO, "refid" },
{ CP_REFTIME, RO, "reftime" },
{ CP_ORG, RO, "org" },
{ CP_REC, RO, "rec" },
{ CP_XMT, RO, "xleave" },
{ CP_REACH, RO, "reach" },
{ CP_UNREACH, RO, "unreach" },
{ CP_TIMER, RO, "timer" },
{ CP_DELAY, RO, "delay" },
{ CP_OFFSET, RO, "offset" },
{ CP_JITTER, RO, "jitter" },
{ CP_DISPERSION, RO, "dispersion" },
{ CP_KEYID, RO, "keyid" },
{ CP_FILTDELAY, RO, "filtdelay=" },
{ CP_FILTOFFSET, RO, "filtoffset=" },
{ CP_PMODE, RO, "pmode" },
{ CP_RECEIVED, RO, "received"},
{ CP_SENT, RO, "sent" },
{ CP_FILTERROR, RO, "filtdisp=" },
{ CP_FLASH, RO, "flash" },
{ CP_TTL, RO, "ttl" },
{ CP_VARLIST, RO, "peer_var_list" },
{ CP_IN, RO, "in" },
{ CP_OUT, RO, "out" },
{ CP_RATE, RO, "headway" },
{ CP_BIAS, RO, "bias" },
#ifdef OPENSSL
{ CP_FLAGS, RO, "flags" },
{ CP_HOST, RO, "host" },
{ CP_VALID, RO, "valid" },
{ CP_INITSEQ, RO, "initsequence" },
{ CP_INITKEY, RO, "initkey" },
{ CP_INITTSP, RO, "timestamp" },
{ CP_SIGNATURE, RO, "signature" },
#endif
{ 0, EOV, "" }
};
static u_char def_peer_var[] = {
CP_SRCADR,
CP_SRCPORT,
CP_DSTADR,
CP_DSTPORT,
CP_OUT,
CP_IN,
CP_LEAP,
CP_STRATUM,
CP_PRECISION,
CP_ROOTDELAY,
CP_ROOTDISPERSION,
CP_REFID,
CP_REFTIME,
CP_REC,
CP_REACH,
CP_UNREACH,
CP_HMODE,
CP_PMODE,
CP_HPOLL,
CP_PPOLL,
CP_RATE,
CP_FLASH,
CP_KEYID,
CP_TTL,
CP_OFFSET,
CP_DELAY,
CP_DISPERSION,
CP_JITTER,
CP_XMT,
CP_BIAS,
CP_FILTDELAY,
CP_FILTOFFSET,
CP_FILTERROR,
#ifdef OPENSSL
CP_HOST,
CP_FLAGS,
CP_SIGNATURE,
CP_VALID,
CP_INITSEQ,
#endif
0
};
#ifdef REFCLOCK
static struct ctl_var clock_var[] = {
{ 0, PADDING, "" },
{ CC_TYPE, RO, "type" },
{ CC_TIMECODE, RO, "timecode" },
{ CC_POLL, RO, "poll" },
{ CC_NOREPLY, RO, "noreply" },
{ CC_BADFORMAT, RO, "badformat" },
{ CC_BADDATA, RO, "baddata" },
{ CC_FUDGETIME1, RO, "fudgetime1" },
{ CC_FUDGETIME2, RO, "fudgetime2" },
{ CC_FUDGEVAL1, RO, "stratum" },
{ CC_FUDGEVAL2, RO, "refid" },
{ CC_FLAGS, RO, "flags" },
{ CC_DEVICE, RO, "device" },
{ CC_VARLIST, RO, "clock_var_list" },
{ 0, EOV, "" }
};
static u_char def_clock_var[] = {
CC_DEVICE,
CC_TYPE,
CC_TIMECODE,
CC_POLL,
CC_NOREPLY,
CC_BADFORMAT,
CC_BADDATA,
CC_FUDGETIME1,
CC_FUDGETIME2,
CC_FUDGEVAL1,
CC_FUDGEVAL2,
CC_FLAGS,
0
};
#endif
#ifndef HAVE_UNAME
# ifndef STR_SYSTEM
# define STR_SYSTEM "UNIX"
# endif
# ifndef STR_PROCESSOR
# define STR_PROCESSOR "unknown"
# endif
static char str_system[] = STR_SYSTEM;
static char str_processor[] = STR_PROCESSOR;
#else
# include <sys/utsname.h>
static struct utsname utsnamebuf;
#endif
struct ctl_trap ctl_trap[CTL_MAXTRAPS];
int num_ctl_traps;
#define TRAP_TYPE_CONFIG 0
#define TRAP_TYPE_PRIO 1
#define TRAP_TYPE_NONPRIO 2
static u_char clocktypes[] = {
CTL_SST_TS_NTP,
CTL_SST_TS_LOCAL,
CTL_SST_TS_UHF,
CTL_SST_TS_HF,
CTL_SST_TS_LF,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_HF,
CTL_SST_TS_LF,
CTL_SST_TS_LF,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_ATOM,
CTL_SST_TS_LF,
CTL_SST_TS_NTP,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_TELEPHONE,
CTL_SST_TS_HF,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_ATOM,
CTL_SST_TS_NTP,
CTL_SST_TS_NTP,
CTL_SST_TS_NTP,
CTL_SST_TS_UHF,
CTL_SST_TS_TELEPHONE,
CTL_SST_TS_TELEPHONE,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_LF,
CTL_SST_TS_LF,
CTL_SST_TS_LF,
CTL_SST_TS_LF,
CTL_SST_TS_HF,
CTL_SST_TS_LF,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_LF,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
CTL_SST_TS_UHF,
};
keyid_t ctl_auth_keyid;
static u_char ctl_sys_last_event;
static u_char ctl_sys_num_events;
u_long ctltimereset;
u_long numctlreq;
u_long numctlbadpkts;
u_long numctlresponses;
u_long numctlfrags;
u_long numctlerrors;
u_long numctltooshort;
u_long numctlinputresp;
u_long numctlinputfrag;
u_long numctlinputerr;
u_long numctlbadoffset;
u_long numctlbadversion;
u_long numctldatatooshort;
u_long numctlbadop;
u_long numasyncmsgs;
static struct ntp_control rpkt;
static u_char res_version;
static u_char res_opcode;
static associd_t res_associd;
static int res_offset;
static u_char * datapt;
static u_char * dataend;
static int datalinelen;
static int datanotbinflag;
static sockaddr_u *rmt_addr;
static struct interface *lcl_inter;
static u_char res_authenticate;
static u_char res_authokay;
static keyid_t res_keyid;
#define MAXDATALINELEN (72)
static u_char res_async;
static char *reqpt;
static char *reqend;
void
init_control(void)
{
int i;
#ifdef HAVE_UNAME
uname(&utsnamebuf);
#endif
ctl_clr_stats();
ctl_auth_keyid = 0;
ctl_sys_last_event = EVNT_UNSPEC;
ctl_sys_num_events = 0;
num_ctl_traps = 0;
for (i = 0; i < CTL_MAXTRAPS; i++)
ctl_trap[i].tr_flags = 0;
}
static void
ctl_error(
int errcode
)
{
DPRINTF(3, ("sending control error %d\n", errcode));
rpkt.r_m_e_op = (u_char) (CTL_RESPONSE|CTL_ERROR|(res_opcode &
CTL_OP_MASK));
rpkt.status = htons((u_short) ((errcode<<8) & 0xff00));
rpkt.count = 0;
if (res_authenticate && sys_authenticate) {
int maclen;
*(u_int32 *)((u_char *)&rpkt + CTL_HEADER_LEN) =
htonl(res_keyid);
maclen = authencrypt(res_keyid, (u_int32 *)&rpkt,
CTL_HEADER_LEN);
sendpkt(rmt_addr, lcl_inter, -2, (struct pkt *)&rpkt,
CTL_HEADER_LEN + maclen);
} else {
sendpkt(rmt_addr, lcl_inter, -3, (struct pkt *)&rpkt,
CTL_HEADER_LEN);
}
numctlerrors++;
}
void
save_config(
struct recvbuf *rbufp,
int restrict_mask
)
{
char reply[128];
#ifdef SAVECONFIG
char filespec[128];
char filename[128];
char fullpath[512];
const char savedconfig_eq[] = "savedconfig=";
char savedconfig[sizeof(savedconfig_eq) + sizeof(filename)];
time_t now;
int fd;
FILE *fptr;
#endif
if (restrict_mask & RES_NOMODIFY) {
snprintf(reply, sizeof(reply),
"saveconfig prohibited by restrict ... nomodify");
ctl_putdata(reply, strlen(reply), 0);
ctl_flushpkt(0);
msyslog(LOG_NOTICE,
"saveconfig from %s rejected due to nomodify restriction",
stoa(&rbufp->recv_srcadr));
return;
}
#ifdef SAVECONFIG
if (NULL == saveconfigdir) {
snprintf(reply, sizeof(reply),
"saveconfig prohibited, no saveconfigdir configured");
ctl_putdata(reply, strlen(reply), 0);
ctl_flushpkt(0);
msyslog(LOG_NOTICE,
"saveconfig from %s rejected, no saveconfigdir",
stoa(&rbufp->recv_srcadr));
return;
}
if (0 == reqend - reqpt)
return;
strncpy(filespec, reqpt, sizeof(filespec));
filespec[sizeof(filespec) - 1] = '\0';
time(&now);
if (0 == strftime(filename, sizeof(filename), filespec,
localtime(&now)))
strncpy(filename, filespec, sizeof(filename));
filename[sizeof(filename) - 1] = '\0';
if (strchr(filename, '\\') || strchr(filename, '/')) {
snprintf(reply, sizeof(reply),
"saveconfig does not allow directory in filename");
ctl_putdata(reply, strlen(reply), 0);
ctl_flushpkt(0);
msyslog(LOG_NOTICE,
"saveconfig with path from %s rejected",
stoa(&rbufp->recv_srcadr));
return;
}
snprintf(fullpath, sizeof(fullpath), "%s%s",
saveconfigdir, filename);
fd = open(fullpath, O_CREAT | O_TRUNC | O_WRONLY,
S_IRUSR | S_IWUSR);
if (-1 == fd)
fptr = NULL;
else
fptr = fdopen(fd, "w");
if (NULL == fptr || -1 == dump_all_config_trees(fptr, 1)) {
snprintf(reply, sizeof(reply),
"Unable to save configuration to file %s",
filename);
msyslog(LOG_ERR,
"saveconfig %s from %s failed", filename,
stoa(&rbufp->recv_srcadr));
} else {
snprintf(reply, sizeof(reply),
"Configuration saved to %s", filename);
msyslog(LOG_NOTICE,
"Configuration saved to %s (requested by %s)",
fullpath, stoa(&rbufp->recv_srcadr));
snprintf(savedconfig, sizeof(savedconfig), "%s%s",
savedconfig_eq, filename);
set_sys_var(savedconfig, strlen(savedconfig) + 1, RO);
}
if (NULL != fptr)
fclose(fptr);
#else
snprintf(reply, sizeof(reply),
"saveconfig unavailable, configured with --disable-saveconfig");
#endif
ctl_putdata(reply, strlen(reply), 0);
ctl_flushpkt(0);
}
void
process_control(
struct recvbuf *rbufp,
int restrict_mask
)
{
register struct ntp_control *pkt;
register int req_count;
register int req_data;
register struct ctl_proc *cc;
int properlen;
int maclen;
DPRINTF(3, ("in process_control()\n"));
numctlreq++;
rmt_addr = &rbufp->recv_srcadr;
lcl_inter = rbufp->dstadr;
pkt = (struct ntp_control *)&rbufp->recv_pkt;
if (rbufp->recv_length < CTL_HEADER_LEN
|| pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR)
|| pkt->offset != 0) {
DPRINTF(1, ("invalid format in control packet\n"));
if (rbufp->recv_length < CTL_HEADER_LEN)
numctltooshort++;
if (pkt->r_m_e_op & CTL_RESPONSE)
numctlinputresp++;
if (pkt->r_m_e_op & CTL_MORE)
numctlinputfrag++;
if (pkt->r_m_e_op & CTL_ERROR)
numctlinputerr++;
if (pkt->offset != 0)
numctlbadoffset++;
return;
}
res_version = PKT_VERSION(pkt->li_vn_mode);
if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) {
DPRINTF(1, ("unknown version %d in control packet\n",
res_version));
numctlbadversion++;
return;
}
rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version,
MODE_CONTROL);
res_opcode = pkt->r_m_e_op;
rpkt.sequence = pkt->sequence;
rpkt.associd = pkt->associd;
rpkt.status = 0;
res_offset = 0;
res_associd = htons(pkt->associd);
res_async = 0;
res_authenticate = 0;
res_keyid = 0;
res_authokay = 0;
req_count = (int)ntohs(pkt->count);
datanotbinflag = 0;
datalinelen = 0;
datapt = rpkt.data;
dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
if ((rbufp->recv_length & 0x3) != 0)
DPRINTF(3, ("Control packet length %d unrounded\n",
rbufp->recv_length));
req_data = rbufp->recv_length - CTL_HEADER_LEN;
if (req_data < req_count || rbufp->recv_length & 0x3) {
ctl_error(CERR_BADFMT);
numctldatatooshort++;
return;
}
properlen = req_count + CTL_HEADER_LEN;
properlen = (properlen + 7) & ~7;
maclen = rbufp->recv_length - properlen;
if ((rbufp->recv_length & 3) == 0 &&
maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN &&
sys_authenticate) {
res_authenticate = 1;
res_keyid = ntohl(*(u_int32 *)((u_char *)pkt +
properlen));
DPRINTF(3, ("recv_len %d, properlen %d, wants auth with keyid %08x, MAC length=%d\n",
rbufp->recv_length, properlen, res_keyid,
maclen));
if (!authistrusted(res_keyid))
DPRINTF(3, ("invalid keyid %08x\n", res_keyid));
else if (authdecrypt(res_keyid, (u_int32 *)pkt,
rbufp->recv_length - maclen,
maclen)) {
DPRINTF(3, ("authenticated okay\n"));
res_authokay = 1;
} else {
DPRINTF(3, ("authentication failed\n"));
res_keyid = 0;
}
}
reqpt = (char *)pkt->data;
reqend = reqpt + req_count;
for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) {
if (cc->control_code == res_opcode) {
DPRINTF(3, ("opcode %d, found command handler\n",
res_opcode));
if (cc->flags == AUTH
&& (!res_authokay
|| res_keyid != ctl_auth_keyid)) {
ctl_error(CERR_PERMISSION);
return;
}
(cc->handler)(rbufp, restrict_mask);
return;
}
}
numctlbadop++;
ctl_error(CERR_BADOP);
return;
}
u_short
ctlpeerstatus(
register struct peer *peer
)
{
u_short status;
status = peer->status;
if (!(peer->flags & FLAG_PREEMPT))
status |= CTL_PST_CONFIG;
if (peer->keyid != 0)
status |= CTL_PST_AUTHENABLE;
if (peer->flags & FLAG_AUTHENTIC)
status |= CTL_PST_AUTHENTIC;
if (peer->reach != 0)
status |= CTL_PST_REACH;
if (peer->cast_flags & (MDF_BCAST | MDF_MCAST | MDF_ACAST))
status |= CTL_PST_BCAST;
return (u_short)CTL_PEER_STATUS(status, peer->num_events,
peer->last_event);
}
#ifdef REFCLOCK
static u_short
ctlclkstatus(
struct refclockstat *this_clock
)
{
return (u_short)CTL_PEER_STATUS(0, this_clock->lastevent,
this_clock->currentstatus);
}
#endif
u_short
ctlsysstatus(void)
{
register u_char this_clock;
this_clock = CTL_SST_TS_UNSPEC;
#ifdef REFCLOCK
if (sys_peer != 0) {
if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) {
this_clock = sys_peer->sstclktype;
} else {
if (sys_peer->refclktype < sizeof(clocktypes))
this_clock =
clocktypes[sys_peer->refclktype];
}
}
#else
if (sys_peer != 0)
this_clock = CTL_SST_TS_NTP;
#endif
return (u_short)CTL_SYS_STATUS(sys_leap, this_clock,
ctl_sys_num_events, ctl_sys_last_event);
}
static void
ctl_flushpkt(
int more
)
{
int dlen;
int sendlen;
if (!more && datanotbinflag) {
*datapt++ = '\r';
*datapt++ = '\n';
}
dlen = datapt - (u_char *)rpkt.data;
sendlen = dlen + CTL_HEADER_LEN;
while (sendlen & 0x3) {
*datapt++ = '\0';
sendlen++;
}
rpkt.r_m_e_op = (u_char)(CTL_RESPONSE|more|(res_opcode &
CTL_OP_MASK));
rpkt.count = htons((u_short) dlen);
rpkt.offset = htons( (u_short) res_offset);
if (res_async) {
register int i;
for (i = 0; i < CTL_MAXTRAPS; i++) {
if (ctl_trap[i].tr_flags & TRAP_INUSE) {
rpkt.li_vn_mode =
PKT_LI_VN_MODE(sys_leap,
ctl_trap[i].tr_version,
MODE_CONTROL);
rpkt.sequence =
htons(ctl_trap[i].tr_sequence);
sendpkt(&ctl_trap[i].tr_addr,
ctl_trap[i].tr_localaddr, -4,
(struct pkt *)&rpkt, sendlen);
if (!more)
ctl_trap[i].tr_sequence++;
numasyncmsgs++;
}
}
} else {
if (res_authenticate && sys_authenticate) {
int maclen;
int totlen = sendlen;
keyid_t keyid = htonl(res_keyid);
while (totlen & 7) {
*datapt++ = '\0';
totlen++;
}
memcpy(datapt, &keyid, sizeof keyid);
maclen = authencrypt(res_keyid,
(u_int32 *)&rpkt, totlen);
sendpkt(rmt_addr, lcl_inter, -5,
(struct pkt *)&rpkt, totlen + maclen);
} else {
sendpkt(rmt_addr, lcl_inter, -6,
(struct pkt *)&rpkt, sendlen);
}
if (more)
numctlfrags++;
else
numctlresponses++;
}
res_offset += dlen;
datapt = (u_char *)rpkt.data;
}
static void
ctl_putdata(
const char *dp,
unsigned int dlen,
int bin
)
{
int overhead;
overhead = 0;
if (!bin) {
datanotbinflag = 1;
overhead = 3;
if (datapt != rpkt.data) {
*datapt++ = ',';
datalinelen++;
if ((dlen + datalinelen + 1) >= MAXDATALINELEN)
{
*datapt++ = '\r';
*datapt++ = '\n';
datalinelen = 0;
} else {
*datapt++ = ' ';
datalinelen++;
}
}
}
if (dlen + overhead + datapt > dataend) {
ctl_flushpkt(CTL_MORE);
}
memmove((char *)datapt, dp, (unsigned)dlen);
datapt += dlen;
datalinelen += dlen;
}
static void
ctl_putstr(
const char *tag,
const char *data,
unsigned int len
)
{
register char *cp;
register const char *cq;
char buffer[400];
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
if (len > 0) {
*cp++ = '=';
*cp++ = '"';
if (len > (int) (sizeof(buffer) - (cp - buffer) - 1))
len = sizeof(buffer) - (cp - buffer) - 1;
memmove(cp, data, (unsigned)len);
cp += len;
*cp++ = '"';
}
ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
}
static void
ctl_putdbl(
const char *tag,
double ts
)
{
register char *cp;
register const char *cq;
char buffer[200];
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
*cp++ = '=';
(void)sprintf(cp, "%.3f", ts);
while (*cp != '\0')
cp++;
ctl_putdata(buffer, (unsigned)(cp - buffer), 0);
}
static void
ctl_putuint(
const char *tag,
u_long uval
)
{
register char *cp;
register const char *cq;
char buffer[200];
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
*cp++ = '=';
(void) sprintf(cp, "%lu", uval);
while (*cp != '\0')
cp++;
ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
}
static void
ctl_putfs(
const char *tag,
tstamp_t uval
)
{
register char *cp;
register const char *cq;
char buffer[200];
struct tm *tm = NULL;
time_t fstamp;
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
*cp++ = '=';
fstamp = uval - JAN_1970;
tm = gmtime(&fstamp);
if (tm == NULL)
return;
sprintf(cp, "%04d%02d%02d%02d%02d", tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
while (*cp != '\0')
cp++;
ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
}
static void
ctl_puthex(
const char *tag,
u_long uval
)
{
register char *cp;
register const char *cq;
char buffer[200];
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
*cp++ = '=';
(void) sprintf(cp, "0x%lx", uval);
while (*cp != '\0')
cp++;
ctl_putdata(buffer,(unsigned)( cp - buffer ), 0);
}
static void
ctl_putint(
const char *tag,
long ival
)
{
register char *cp;
register const char *cq;
char buffer[200];
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
*cp++ = '=';
(void) sprintf(cp, "%ld", ival);
while (*cp != '\0')
cp++;
ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
}
static void
ctl_putts(
const char *tag,
l_fp *ts
)
{
register char *cp;
register const char *cq;
char buffer[200];
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
*cp++ = '=';
(void) sprintf(cp, "0x%08lx.%08lx", ts->l_ui & 0xffffffffUL,
ts->l_uf & 0xffffffffUL);
while (*cp != '\0')
cp++;
ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
}
static void
ctl_putadr(
const char *tag,
u_int32 addr32,
sockaddr_u *addr
)
{
register char *cp;
register const char *cq;
char buffer[200];
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
*cp++ = '=';
if (addr == NULL)
cq = numtoa(addr32);
else
cq = stoa(addr);
while (*cq != '\0')
*cp++ = *cq++;
ctl_putdata(buffer, (unsigned)(cp - buffer), 0);
}
static void
ctl_putid(
const char *tag,
char *id
)
{
register char *cp;
register const char *cq;
char buffer[200];
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
*cp++ = '=';
cq = id;
while (*cq != '\0' && (cq - id) < 4)
*cp++ = *cq++;
ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
}
static void
ctl_putarray(
const char *tag,
double *arr,
int start
)
{
register char *cp;
register const char *cq;
char buffer[200];
int i;
cp = buffer;
cq = tag;
while (*cq != '\0')
*cp++ = *cq++;
i = start;
do {
if (i == 0)
i = NTP_SHIFT;
i--;
(void)sprintf(cp, " %.2f", arr[i] * 1e3);
while (*cp != '\0')
cp++;
} while(i != start);
ctl_putdata(buffer, (unsigned)(cp - buffer), 0);
}
static void
ctl_putsys(
int varid
)
{
l_fp tmp;
char str[256];
#ifdef OPENSSL
struct cert_info *cp;
char cbuf[256];
#endif
switch (varid) {
case CS_LEAP:
ctl_putuint(sys_var[CS_LEAP].text, sys_leap);
break;
case CS_STRATUM:
ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum);
break;
case CS_PRECISION:
ctl_putint(sys_var[CS_PRECISION].text, sys_precision);
break;
case CS_ROOTDELAY:
ctl_putdbl(sys_var[CS_ROOTDELAY].text, sys_rootdelay *
1e3);
break;
case CS_ROOTDISPERSION:
ctl_putdbl(sys_var[CS_ROOTDISPERSION].text,
sys_rootdisp * 1e3);
break;
case CS_REFID:
if (sys_stratum > 1 && sys_stratum < STRATUM_UNSPEC)
ctl_putadr(sys_var[CS_REFID].text, sys_refid, NULL);
else
ctl_putid(sys_var[CS_REFID].text,
(char *)&sys_refid);
break;
case CS_REFTIME:
ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime);
break;
case CS_POLL:
ctl_putuint(sys_var[CS_POLL].text, sys_poll);
break;
case CS_PEERID:
if (sys_peer == NULL)
ctl_putuint(sys_var[CS_PEERID].text, 0);
else
ctl_putuint(sys_var[CS_PEERID].text,
sys_peer->associd);
break;
case CS_OFFSET:
ctl_putdbl(sys_var[CS_OFFSET].text, last_offset * 1e3);
break;
case CS_DRIFT:
ctl_putdbl(sys_var[CS_DRIFT].text, drift_comp * 1e6);
break;
case CS_JITTER:
ctl_putdbl(sys_var[CS_JITTER].text, sys_jitter * 1e3);
break;
case CS_ERROR:
ctl_putdbl(sys_var[CS_ERROR].text, clock_jitter * 1e3);
break;
case CS_CLOCK:
get_systime(&tmp);
ctl_putts(sys_var[CS_CLOCK].text, &tmp);
break;
case CS_PROCESSOR:
#ifndef HAVE_UNAME
ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor,
sizeof(str_processor) - 1);
#else
ctl_putstr(sys_var[CS_PROCESSOR].text,
utsnamebuf.machine, strlen(utsnamebuf.machine));
#endif
break;
case CS_SYSTEM:
#ifndef HAVE_UNAME
ctl_putstr(sys_var[CS_SYSTEM].text, str_system,
sizeof(str_system) - 1);
#else
sprintf(str, "%s/%s", utsnamebuf.sysname, utsnamebuf.release);
ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str));
#endif
break;
case CS_VERSION:
ctl_putstr(sys_var[CS_VERSION].text, Version,
strlen(Version));
break;
case CS_STABIL:
ctl_putdbl(sys_var[CS_STABIL].text, clock_stability *
1e6);
break;
case CS_VARLIST:
{
char buf[CTL_MAX_DATA_LEN];
register char *s, *t, *be;
register const char *ss;
register int i;
register struct ctl_var *k;
s = buf;
be = buf + sizeof(buf);
if (s + strlen(sys_var[CS_VARLIST].text) + 4 > be)
break;
snprintf(s, sizeof(buf), "%s=\"",
sys_var[CS_VARLIST].text);
s += strlen(s);
t = s;
for (k = sys_var; !(k->flags & EOV); k++) {
if (k->flags & PADDING)
continue;
i = strlen(k->text);
if (s+i+1 >= be)
break;
if (s != t)
*s++ = ',';
memcpy(s, k->text, i);
s += i;
}
for (k = ext_sys_var; k && !(k->flags & EOV);
k++) {
if (k->flags & PADDING)
continue;
ss = k->text;
if (!ss)
continue;
while (*ss && *ss != '=')
ss++;
i = ss - k->text;
if (s + i + 1 >= be)
break;
if (s != t)
*s++ = ',';
memcpy(s, k->text,
(unsigned)i);
s += i;
}
if (s+2 >= be)
break;
*s++ = '"';
*s = '\0';
ctl_putdata(buf, (unsigned)( s - buf ),
0);
}
break;
case CS_TAI:
if (sys_tai > 0)
ctl_putuint(sys_var[CS_TAI].text, sys_tai);
break;
case CS_LEAPTAB:
if (leap_sec > 0)
ctl_putfs(sys_var[CS_LEAPTAB].text,
leap_sec);
break;
case CS_LEAPEND:
if (leap_expire > 0)
ctl_putfs(sys_var[CS_LEAPEND].text,
leap_expire);
break;
case CS_RATE:
ctl_putuint(sys_var[CS_RATE].text, ntp_minpoll);
break;
#ifdef OPENSSL
case CS_FLAGS:
if (crypto_flags)
ctl_puthex(sys_var[CS_FLAGS].text,
crypto_flags);
break;
case CS_DIGEST:
if (crypto_flags) {
strcpy(str, OBJ_nid2ln(crypto_nid));
ctl_putstr(sys_var[CS_DIGEST].text, str,
strlen(str));
}
break;
case CS_SIGNATURE:
if (crypto_flags) {
const EVP_MD *dp;
dp = EVP_get_digestbynid(crypto_flags >> 16);
strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)));
ctl_putstr(sys_var[CS_SIGNATURE].text, str,
strlen(str));
}
break;
case CS_HOST:
if (sys_hostname != NULL)
ctl_putstr(sys_var[CS_HOST].text, sys_hostname,
strlen(sys_hostname));
break;
case CS_GROUP:
if (sys_groupname != NULL)
ctl_putstr(sys_var[CS_GROUP].text, sys_groupname,
strlen(sys_groupname));
break;
case CS_CERTIF:
for (cp = cinfo; cp != NULL; cp = cp->link) {
snprintf(cbuf, sizeof(cbuf), "%s %s 0x%x",
cp->subject, cp->issuer, cp->flags);
ctl_putstr(sys_var[CS_CERTIF].text, cbuf,
strlen(cbuf));
ctl_putfs(sys_var[CS_REVTIME].text, cp->last);
}
break;
case CS_PUBLIC:
if (hostval.tstamp != 0)
ctl_putfs(sys_var[CS_PUBLIC].text,
ntohl(hostval.tstamp));
break;
#endif
}
}
static void
ctl_putpeer(
int varid,
struct peer *peer
)
{
int temp;
#ifdef OPENSSL
char str[256];
struct autokey *ap;
#endif
switch (varid) {
case CP_CONFIG:
ctl_putuint(peer_var[CP_CONFIG].text,
(unsigned)((peer->flags & FLAG_PREEMPT) == 0));
break;
case CP_AUTHENABLE:
ctl_putuint(peer_var[CP_AUTHENABLE].text,
(unsigned)(peer->keyid != 0));
break;
case CP_AUTHENTIC:
ctl_putuint(peer_var[CP_AUTHENTIC].text,
(unsigned)((peer->flags & FLAG_AUTHENTIC) != 0));
break;
case CP_SRCADR:
ctl_putadr(peer_var[CP_SRCADR].text, 0,
&peer->srcadr);
break;
case CP_SRCPORT:
ctl_putuint(peer_var[CP_SRCPORT].text,
ntohs(((struct sockaddr_in*)&peer->srcadr)->sin_port));
break;
case CP_DSTADR:
if (peer->dstadr) {
ctl_putadr(peer_var[CP_DSTADR].text, 0,
&(peer->dstadr->sin));
} else {
ctl_putadr(peer_var[CP_DSTADR].text, 0,
NULL);
}
break;
case CP_DSTPORT:
ctl_putuint(peer_var[CP_DSTPORT].text,
(u_long)(peer->dstadr ?
ntohs(((struct sockaddr_in*)&peer->dstadr->sin)->sin_port) : 0));
break;
case CP_IN:
if (peer->r21 > 0)
ctl_putdbl(peer_var[CP_IN].text,
peer->r21 / 1e3);
break;
case CP_OUT:
if (peer->r34 >0)
ctl_putdbl(peer_var[CP_OUT].text,
peer->r34 / 1e3);
break;
case CP_RATE:
ctl_putuint(peer_var[CP_RATE].text, peer->throttle);
break;
case CP_LEAP:
ctl_putuint(peer_var[CP_LEAP].text, peer->leap);
break;
case CP_HMODE:
ctl_putuint(peer_var[CP_HMODE].text, peer->hmode);
break;
case CP_STRATUM:
ctl_putuint(peer_var[CP_STRATUM].text, peer->stratum);
break;
case CP_PPOLL:
ctl_putuint(peer_var[CP_PPOLL].text, peer->ppoll);
break;
case CP_HPOLL:
ctl_putuint(peer_var[CP_HPOLL].text, peer->hpoll);
break;
case CP_PRECISION:
ctl_putint(peer_var[CP_PRECISION].text,
peer->precision);
break;
case CP_ROOTDELAY:
ctl_putdbl(peer_var[CP_ROOTDELAY].text,
peer->rootdelay * 1e3);
break;
case CP_ROOTDISPERSION:
ctl_putdbl(peer_var[CP_ROOTDISPERSION].text,
peer->rootdisp * 1e3);
break;
case CP_REFID:
if (peer->flags & FLAG_REFCLOCK) {
ctl_putid(peer_var[CP_REFID].text,
(char *)&peer->refid);
} else {
if (peer->stratum > 1 && peer->stratum <
STRATUM_UNSPEC)
ctl_putadr(peer_var[CP_REFID].text,
peer->refid, NULL);
else
ctl_putid(peer_var[CP_REFID].text,
(char *)&peer->refid);
}
break;
case CP_REFTIME:
ctl_putts(peer_var[CP_REFTIME].text, &peer->reftime);
break;
case CP_ORG:
ctl_putts(peer_var[CP_ORG].text, &peer->aorg);
break;
case CP_REC:
ctl_putts(peer_var[CP_REC].text, &peer->dst);
break;
case CP_XMT:
if (peer->xleave != 0)
ctl_putdbl(peer_var[CP_XMT].text, peer->xleave *
1e3);
break;
case CP_BIAS:
if (peer->bias != 0)
ctl_putdbl(peer_var[CP_BIAS].text, peer->bias *
1e3);
break;
case CP_REACH:
ctl_puthex(peer_var[CP_REACH].text, peer->reach);
break;
case CP_FLASH:
temp = peer->flash;
ctl_puthex(peer_var[CP_FLASH].text, temp);
break;
case CP_TTL:
if (peer->ttl > 0)
ctl_putint(peer_var[CP_TTL].text,
sys_ttl[peer->ttl]);
break;
case CP_UNREACH:
ctl_putuint(peer_var[CP_UNREACH].text, peer->unreach);
break;
case CP_TIMER:
ctl_putuint(peer_var[CP_TIMER].text,
peer->nextdate - current_time);
break;
case CP_DELAY:
ctl_putdbl(peer_var[CP_DELAY].text, peer->delay * 1e3);
break;
case CP_OFFSET:
ctl_putdbl(peer_var[CP_OFFSET].text, peer->offset *
1e3);
break;
case CP_JITTER:
ctl_putdbl(peer_var[CP_JITTER].text, peer->jitter *
1e3);
break;
case CP_DISPERSION:
ctl_putdbl(peer_var[CP_DISPERSION].text, peer->disp *
1e3);
break;
case CP_KEYID:
if (peer->keyid > NTP_MAXKEY)
ctl_puthex(peer_var[CP_KEYID].text,
peer->keyid);
else
ctl_putuint(peer_var[CP_KEYID].text,
peer->keyid);
break;
case CP_FILTDELAY:
ctl_putarray(peer_var[CP_FILTDELAY].text,
peer->filter_delay, (int)peer->filter_nextpt);
break;
case CP_FILTOFFSET:
ctl_putarray(peer_var[CP_FILTOFFSET].text,
peer->filter_offset, (int)peer->filter_nextpt);
break;
case CP_FILTERROR:
ctl_putarray(peer_var[CP_FILTERROR].text,
peer->filter_disp, (int)peer->filter_nextpt);
break;
case CP_PMODE:
ctl_putuint(peer_var[CP_PMODE].text, peer->pmode);
break;
case CP_RECEIVED:
ctl_putuint(peer_var[CP_RECEIVED].text, peer->received);
break;
case CP_SENT:
ctl_putuint(peer_var[CP_SENT].text, peer->sent);
break;
case CP_VARLIST:
{
char buf[CTL_MAX_DATA_LEN];
register char *s, *t, *be;
register int i;
register struct ctl_var *k;
s = buf;
be = buf + sizeof(buf);
if (s + strlen(peer_var[CP_VARLIST].text) + 4 > be)
break;
snprintf(s, sizeof(buf), "%s=\"",
peer_var[CP_VARLIST].text);
s += strlen(s);
t = s;
for (k = peer_var; !(k->flags & EOV); k++) {
if (k->flags & PADDING)
continue;
i = strlen(k->text);
if (s + i + 1 >= be)
break;
if (s != t)
*s++ = ',';
memcpy(s, k->text, i);
s += i;
}
if (s+2 >= be)
break;
*s++ = '"';
*s = '\0';
ctl_putdata(buf, (unsigned)(s - buf), 0);
}
break;
#ifdef OPENSSL
case CP_FLAGS:
if (peer->crypto)
ctl_puthex(peer_var[CP_FLAGS].text, peer->crypto);
break;
case CP_SIGNATURE:
if (peer->crypto) {
const EVP_MD *dp;
dp = EVP_get_digestbynid(peer->crypto >> 16);
strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)));
ctl_putstr(peer_var[CP_SIGNATURE].text, str,
strlen(str));
}
break;
case CP_HOST:
if (peer->subject != NULL)
ctl_putstr(peer_var[CP_HOST].text,
peer->subject, strlen(peer->subject));
break;
case CP_VALID:
break;
case CP_INITSEQ:
if ((ap = (struct autokey *)peer->recval.ptr) == NULL)
break;
ctl_putint(peer_var[CP_INITSEQ].text, ap->seq);
ctl_puthex(peer_var[CP_INITKEY].text, ap->key);
ctl_putfs(peer_var[CP_INITTSP].text,
ntohl(peer->recval.tstamp));
break;
#endif
}
}
#ifdef REFCLOCK
static void
ctl_putclock(
int varid,
struct refclockstat *clock_stat,
int mustput
)
{
switch(varid) {
case CC_TYPE:
if (mustput || clock_stat->clockdesc == NULL
|| *(clock_stat->clockdesc) == '\0') {
ctl_putuint(clock_var[CC_TYPE].text, clock_stat->type);
}
break;
case CC_TIMECODE:
ctl_putstr(clock_var[CC_TIMECODE].text,
clock_stat->p_lastcode,
(unsigned)clock_stat->lencode);
break;
case CC_POLL:
ctl_putuint(clock_var[CC_POLL].text, clock_stat->polls);
break;
case CC_NOREPLY:
ctl_putuint(clock_var[CC_NOREPLY].text,
clock_stat->noresponse);
break;
case CC_BADFORMAT:
ctl_putuint(clock_var[CC_BADFORMAT].text,
clock_stat->badformat);
break;
case CC_BADDATA:
ctl_putuint(clock_var[CC_BADDATA].text,
clock_stat->baddata);
break;
case CC_FUDGETIME1:
if (mustput || (clock_stat->haveflags & CLK_HAVETIME1))
ctl_putdbl(clock_var[CC_FUDGETIME1].text,
clock_stat->fudgetime1 * 1e3);
break;
case CC_FUDGETIME2:
if (mustput || (clock_stat->haveflags & CLK_HAVETIME2)) ctl_putdbl(clock_var[CC_FUDGETIME2].text,
clock_stat->fudgetime2 * 1e3);
break;
case CC_FUDGEVAL1:
if (mustput || (clock_stat->haveflags & CLK_HAVEVAL1))
ctl_putint(clock_var[CC_FUDGEVAL1].text,
clock_stat->fudgeval1);
break;
case CC_FUDGEVAL2:
if (mustput || (clock_stat->haveflags & CLK_HAVEVAL2)) {
if (clock_stat->fudgeval1 > 1)
ctl_putadr(clock_var[CC_FUDGEVAL2].text,
(u_int32)clock_stat->fudgeval2, NULL);
else
ctl_putid(clock_var[CC_FUDGEVAL2].text,
(char *)&clock_stat->fudgeval2);
}
break;
case CC_FLAGS:
if (mustput || (clock_stat->haveflags & (CLK_HAVEFLAG1 |
CLK_HAVEFLAG2 | CLK_HAVEFLAG3 | CLK_HAVEFLAG4)))
ctl_putuint(clock_var[CC_FLAGS].text,
clock_stat->flags);
break;
case CC_DEVICE:
if (clock_stat->clockdesc == NULL ||
*(clock_stat->clockdesc) == '\0') {
if (mustput)
ctl_putstr(clock_var[CC_DEVICE].text,
"", 0);
} else {
ctl_putstr(clock_var[CC_DEVICE].text,
clock_stat->clockdesc,
strlen(clock_stat->clockdesc));
}
break;
case CC_VARLIST:
{
char buf[CTL_MAX_DATA_LEN];
register char *s, *t, *be;
register const char *ss;
register int i;
register struct ctl_var *k;
s = buf;
be = buf + sizeof(buf);
if (s + strlen(clock_var[CC_VARLIST].text) + 4 >
be)
break;
snprintf(s, sizeof(buf), "%s=\"",
clock_var[CC_VARLIST].text);
s += strlen(s);
t = s;
for (k = clock_var; !(k->flags & EOV); k++) {
if (k->flags & PADDING)
continue;
i = strlen(k->text);
if (s + i + 1 >= be)
break;
if (s != t)
*s++ = ',';
memcpy(s, k->text, i);
s += i;
}
for (k = clock_stat->kv_list; k && !(k->flags &
EOV); k++) {
if (k->flags & PADDING)
continue;
ss = k->text;
if (!ss)
continue;
while (*ss && *ss != '=')
ss++;
i = ss - k->text;
if (s+i+1 >= be)
break;
if (s != t)
*s++ = ',';
memcpy(s, k->text, (unsigned)i);
s += i;
*s = '\0';
}
if (s+2 >= be)
break;
*s++ = '"';
*s = '\0';
ctl_putdata(buf, (unsigned)( s - buf ), 0);
}
break;
}
}
#endif
static struct ctl_var *
ctl_getitem(
struct ctl_var *var_list,
char **data
)
{
register struct ctl_var *v;
register char *cp;
register char *tp;
static struct ctl_var eol = { 0, EOV, };
static char buf[128];
while (reqpt < reqend && (*reqpt == ',' ||
isspace((unsigned char)*reqpt)))
reqpt++;
if (reqpt >= reqend)
return (0);
if (var_list == (struct ctl_var *)0)
return (&eol);
v = var_list;
cp = reqpt;
while (!(v->flags & EOV)) {
if (!(v->flags & PADDING) && *cp == *(v->text)) {
tp = v->text;
while (*tp != '\0' && *tp != '=' && cp <
reqend && *cp == *tp) {
cp++;
tp++;
}
if ((*tp == '\0') || (*tp == '=')) {
while (cp < reqend && isspace((unsigned char)*cp))
cp++;
if (cp == reqend || *cp == ',') {
buf[0] = '\0';
*data = buf;
if (cp < reqend)
cp++;
reqpt = cp;
return v;
}
if (*cp == '=') {
cp++;
tp = buf;
while (cp < reqend && isspace((unsigned char)*cp))
cp++;
while (cp < reqend && *cp != ',') {
*tp++ = *cp++;
if (tp >= buf + sizeof(buf)) {
ctl_error(CERR_BADFMT);
numctlbadpkts++;
#if 0
msyslog(LOG_WARNING,
"Possible 'ntpdx' exploit from %s:%d (possibly spoofed)\n",
stoa(rmt_addr), SRCPORT(rmt_addr)
);
#endif
return (0);
}
}
if (cp < reqend)
cp++;
*tp-- = '\0';
while (tp >= buf) {
if (!isspace((unsigned int)(*tp)))
break;
*tp-- = '\0';
}
reqpt = cp;
*data = buf;
return (v);
}
}
cp = reqpt;
}
v++;
}
return v;
}
static void
control_unspec(
struct recvbuf *rbufp,
int restrict_mask
)
{
struct peer *peer;
if (res_associd != 0) {
if ((peer = findpeerbyassoc(res_associd)) == 0) {
ctl_error(CERR_BADASSOC);
return;
}
rpkt.status = htons(ctlpeerstatus(peer));
} else {
rpkt.status = htons(ctlsysstatus());
}
ctl_flushpkt(0);
}
static void
read_status(
struct recvbuf *rbufp,
int restrict_mask
)
{
register int i;
register struct peer *peer;
u_short ass_stat[CTL_MAX_DATA_LEN / sizeof(u_short)];
#ifdef DEBUG
if (debug > 2)
printf("read_status: ID %d\n", res_associd);
#endif
if (res_associd == 0) {
register int n;
n = 0;
rpkt.status = htons(ctlsysstatus());
for (i = 0; i < NTP_HASH_SIZE; i++) {
for (peer = assoc_hash[i]; peer != 0;
peer = peer->ass_next) {
ass_stat[n++] = htons(peer->associd);
ass_stat[n++] =
htons(ctlpeerstatus(peer));
if (n ==
CTL_MAX_DATA_LEN/sizeof(u_short)) {
ctl_putdata((char *)ass_stat,
n * sizeof(u_short), 1);
n = 0;
}
}
}
if (n != 0)
ctl_putdata((char *)ass_stat, n *
sizeof(u_short), 1);
ctl_flushpkt(0);
} else {
peer = findpeerbyassoc(res_associd);
if (peer == 0) {
ctl_error(CERR_BADASSOC);
} else {
register u_char *cp;
rpkt.status = htons(ctlpeerstatus(peer));
if (res_authokay)
peer->num_events = 0;
for (cp = def_peer_var; *cp != 0; cp++)
ctl_putpeer((int)*cp, peer);
ctl_flushpkt(0);
}
}
}
static void
read_variables(
struct recvbuf *rbufp,
int restrict_mask
)
{
register struct ctl_var *v;
register int i;
char *valuep;
u_char *wants;
unsigned int gotvar = (CS_MAXCODE > CP_MAXCODE) ? (CS_MAXCODE +
1) : (CP_MAXCODE + 1);
if (res_associd == 0) {
rpkt.status = htons(ctlsysstatus());
if (res_authokay)
ctl_sys_num_events = 0;
gotvar += count_var(ext_sys_var);
wants = (u_char *)emalloc(gotvar);
memset((char *)wants, 0, gotvar);
gotvar = 0;
while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
if (v->flags & EOV) {
if ((v = ctl_getitem(ext_sys_var,
&valuep)) != 0) {
if (v->flags & EOV) {
ctl_error(CERR_UNKNOWNVAR);
free((char *)wants);
return;
}
wants[CS_MAXCODE + 1 +
v->code] = 1;
gotvar = 1;
continue;
} else {
break;
}
}
wants[v->code] = 1;
gotvar = 1;
}
if (gotvar) {
for (i = 1; i <= CS_MAXCODE; i++)
if (wants[i])
ctl_putsys(i);
for (i = 0; ext_sys_var &&
!(ext_sys_var[i].flags & EOV); i++)
if (wants[i + CS_MAXCODE + 1])
ctl_putdata(ext_sys_var[i].text,
strlen(ext_sys_var[i].text),
0);
} else {
register u_char *cs;
register struct ctl_var *kv;
for (cs = def_sys_var; *cs != 0; cs++)
ctl_putsys((int)*cs);
for (kv = ext_sys_var; kv && !(kv->flags & EOV);
kv++)
if (kv->flags & DEF)
ctl_putdata(kv->text,
strlen(kv->text), 0);
}
free((char *)wants);
} else {
register struct peer *peer;
peer = findpeerbyassoc(res_associd);
if (peer == 0) {
ctl_error(CERR_BADASSOC);
return;
}
rpkt.status = htons(ctlpeerstatus(peer));
if (res_authokay)
peer->num_events = 0;
wants = (u_char *)emalloc(gotvar);
memset((char*)wants, 0, gotvar);
gotvar = 0;
while ((v = ctl_getitem(peer_var, &valuep)) != 0) {
if (v->flags & EOV) {
ctl_error(CERR_UNKNOWNVAR);
free((char *)wants);
return;
}
wants[v->code] = 1;
gotvar = 1;
}
if (gotvar) {
for (i = 1; i <= CP_MAXCODE; i++)
if (wants[i])
ctl_putpeer(i, peer);
} else {
register u_char *cp;
for (cp = def_peer_var; *cp != 0; cp++)
ctl_putpeer((int)*cp, peer);
}
free((char *)wants);
}
ctl_flushpkt(0);
}
static void
write_variables(
struct recvbuf *rbufp,
int restrict_mask
)
{
register struct ctl_var *v;
register int ext_var;
char *valuep;
long val = 0;
if (res_associd != 0) {
ctl_error(CERR_PERMISSION);
return;
}
rpkt.status = htons(ctlsysstatus());
while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
ext_var = 0;
if (v->flags & EOV) {
if ((v = ctl_getitem(ext_sys_var, &valuep)) !=
0) {
if (v->flags & EOV) {
ctl_error(CERR_UNKNOWNVAR);
return;
}
ext_var = 1;
} else {
break;
}
}
if (!(v->flags & CAN_WRITE)) {
ctl_error(CERR_PERMISSION);
return;
}
if (!ext_var && (*valuep == '\0' || !atoint(valuep,
&val))) {
ctl_error(CERR_BADFMT);
return;
}
if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) {
ctl_error(CERR_BADVALUE);
return;
}
if (ext_var) {
char *s = (char *)emalloc(strlen(v->text) +
strlen(valuep) + 2);
const char *t;
char *tt = s;
t = v->text;
while (*t && *t != '=')
*tt++ = *t++;
*tt++ = '=';
strcat(tt, valuep);
set_sys_var(s, strlen(s)+1, v->flags);
free(s);
} else {
switch(v->code) {
case CS_LEAP:
default:
ctl_error(CERR_UNSPEC);
return;
}
}
}
ctl_flushpkt(0);
}
static void configure(
struct recvbuf *rbufp,
int restrict_mask
)
{
int data_count, retval, replace_nl;
if (res_associd != 0) {
ctl_error(CERR_BADVALUE);
return;
}
if (restrict_mask & RES_NOMODIFY) {
snprintf(remote_config.err_msg,
sizeof(remote_config.err_msg),
"runtime configuration prohibited by restrict ... nomodify");
ctl_putdata(remote_config.err_msg,
strlen(remote_config.err_msg), 0);
ctl_flushpkt(0);
msyslog(LOG_NOTICE,
"runtime config from %s rejected due to nomodify restriction",
stoa(&rbufp->recv_srcadr));
return;
}
data_count = reqend - reqpt;
memcpy(remote_config.buffer, reqpt, data_count);
if (data_count > 0
&& '\n' != remote_config.buffer[data_count - 1])
remote_config.buffer[data_count++] = '\n';
remote_config.buffer[data_count] = '\0';
remote_config.pos = 0;
remote_config.err_pos = 0;
remote_config.no_errors = 0;
if (data_count > 0
&& '\n' == remote_config.buffer[data_count - 1]) {
remote_config.buffer[data_count - 1] = '\0';
replace_nl = 1;
} else
replace_nl = 0;
DPRINTF(1, ("Got Remote Configuration Command: %s\n",
remote_config.buffer));
msyslog(LOG_NOTICE, "%s config: %s",
stoa(&rbufp->recv_srcadr),
remote_config.buffer);
if (replace_nl)
remote_config.buffer[data_count - 1] = '\n';
config_remotely(&rbufp->recv_srcadr);
if (0 == remote_config.no_errors) {
retval = snprintf(remote_config.err_msg,
sizeof(remote_config.err_msg),
"Config Succeeded");
if (retval > 0)
remote_config.err_pos += retval;
}
ctl_putdata(remote_config.err_msg, remote_config.err_pos, 0);
ctl_flushpkt(0);
DPRINTF(1, ("Reply: %s\n", remote_config.err_msg));
if (remote_config.no_errors > 0)
msyslog(LOG_NOTICE, "%d error in %s config",
remote_config.no_errors,
stoa(&rbufp->recv_srcadr));
}
static void
read_clock_status(
struct recvbuf *rbufp,
int restrict_mask
)
{
#ifndef REFCLOCK
ctl_error(CERR_BADASSOC);
#else
register struct ctl_var *v;
register int i;
register struct peer *peer;
char *valuep;
u_char *wants;
unsigned int gotvar;
struct refclockstat clock_stat;
if (res_associd == 0) {
if (sys_peer != 0 && (sys_peer->flags & FLAG_REFCLOCK))
{
peer = sys_peer;
} else {
peer = 0;
for (i = 0; peer == 0 && i < NTP_HASH_SIZE; i++) {
for (peer = assoc_hash[i]; peer != 0;
peer = peer->ass_next) {
if (peer->flags & FLAG_REFCLOCK)
break;
}
}
if (peer == 0) {
ctl_error(CERR_BADASSOC);
return;
}
}
} else {
peer = findpeerbyassoc(res_associd);
if (peer == 0 || !(peer->flags & FLAG_REFCLOCK)) {
ctl_error(CERR_BADASSOC);
return;
}
}
clock_stat.kv_list = (struct ctl_var *)0;
refclock_control(&peer->srcadr, (struct refclockstat *)0,
&clock_stat);
rpkt.status = htons(ctlclkstatus(&clock_stat));
gotvar = CC_MAXCODE + 1 + count_var(clock_stat.kv_list);
wants = (u_char *)emalloc(gotvar);
memset((char*)wants, 0, gotvar);
gotvar = 0;
while ((v = ctl_getitem(clock_var, &valuep)) != 0) {
if (v->flags & EOV) {
if ((v = ctl_getitem(clock_stat.kv_list,
&valuep)) != 0) {
if (v->flags & EOV) {
ctl_error(CERR_UNKNOWNVAR);
free((char*)wants);
free_varlist(clock_stat.kv_list);
return;
}
wants[CC_MAXCODE + 1 + v->code] = 1;
gotvar = 1;
continue;
} else {
break;
}
}
wants[v->code] = 1;
gotvar = 1;
}
if (gotvar) {
for (i = 1; i <= CC_MAXCODE; i++)
if (wants[i])
ctl_putclock(i, &clock_stat, 1);
for (i = 0; clock_stat.kv_list &&
!(clock_stat.kv_list[i].flags & EOV); i++)
if (wants[i + CC_MAXCODE + 1])
ctl_putdata(clock_stat.kv_list[i].text,
strlen(clock_stat.kv_list[i].text),
0);
} else {
register u_char *cc;
register struct ctl_var *kv;
for (cc = def_clock_var; *cc != 0; cc++)
ctl_putclock((int)*cc, &clock_stat, 0);
for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV);
kv++)
if (kv->flags & DEF)
ctl_putdata(kv->text, strlen(kv->text),
0);
}
free((char*)wants);
free_varlist(clock_stat.kv_list);
ctl_flushpkt(0);
#endif
}
static void
write_clock_status(
struct recvbuf *rbufp,
int restrict_mask
)
{
ctl_error(CERR_PERMISSION);
}
static void
set_trap(
struct recvbuf *rbufp,
int restrict_mask
)
{
int traptype;
if (restrict_mask & RES_NOTRAP) {
ctl_error(CERR_PERMISSION);
return;
}
traptype = TRAP_TYPE_PRIO;
if (restrict_mask & RES_LPTRAP)
traptype = TRAP_TYPE_NONPRIO;
if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype,
(int)res_version))
ctl_error(CERR_NORESOURCE);
ctl_flushpkt(0);
}
static void
unset_trap(
struct recvbuf *rbufp,
int restrict_mask
)
{
int traptype;
traptype = TRAP_TYPE_PRIO;
if (restrict_mask & RES_LPTRAP)
traptype = TRAP_TYPE_NONPRIO;
if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype))
ctl_error(CERR_BADASSOC);
ctl_flushpkt(0);
}
int
ctlsettrap(
sockaddr_u *raddr,
struct interface *linter,
int traptype,
int version
)
{
register struct ctl_trap *tp;
register struct ctl_trap *tptouse;
if ((tp = ctlfindtrap(raddr, linter)) != NULL) {
switch (traptype) {
case TRAP_TYPE_CONFIG:
tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED;
break;
case TRAP_TYPE_PRIO:
if (tp->tr_flags & TRAP_CONFIGURED)
return (1);
tp->tr_flags = TRAP_INUSE;
break;
case TRAP_TYPE_NONPRIO:
if (tp->tr_flags & TRAP_CONFIGURED)
return (1);
tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO;
break;
}
tp->tr_settime = current_time;
tp->tr_resets++;
return (1);
}
tptouse = NULL;
for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
if ((tp->tr_flags & TRAP_INUSE) &&
!(tp->tr_flags & TRAP_CONFIGURED) &&
((tp->tr_settime + CTL_TRAPTIME) > current_time)) {
tp->tr_flags = 0;
num_ctl_traps--;
}
if (!(tp->tr_flags & TRAP_INUSE)) {
tptouse = tp;
} else if (!(tp->tr_flags & TRAP_CONFIGURED)) {
switch (traptype) {
case TRAP_TYPE_CONFIG:
if (tptouse == NULL) {
tptouse = tp;
break;
}
if (tptouse->tr_flags & TRAP_NONPRIO &&
!(tp->tr_flags & TRAP_NONPRIO))
break;
if (!(tptouse->tr_flags & TRAP_NONPRIO)
&& tp->tr_flags & TRAP_NONPRIO) {
tptouse = tp;
break;
}
if (tptouse->tr_origtime <
tp->tr_origtime)
tptouse = tp;
break;
case TRAP_TYPE_PRIO:
if (tp->tr_flags & TRAP_NONPRIO) {
if (tptouse == NULL ||
(tptouse->tr_flags &
TRAP_INUSE &&
tptouse->tr_origtime <
tp->tr_origtime))
tptouse = tp;
}
break;
case TRAP_TYPE_NONPRIO:
break;
}
}
}
if (tptouse == NULL)
return (0);
tptouse->tr_settime = tptouse->tr_origtime = current_time;
tptouse->tr_count = tptouse->tr_resets = 0;
tptouse->tr_sequence = 1;
tptouse->tr_addr = *raddr;
tptouse->tr_localaddr = linter;
tptouse->tr_version = (u_char) version;
tptouse->tr_flags = TRAP_INUSE;
if (traptype == TRAP_TYPE_CONFIG)
tptouse->tr_flags |= TRAP_CONFIGURED;
else if (traptype == TRAP_TYPE_NONPRIO)
tptouse->tr_flags |= TRAP_NONPRIO;
num_ctl_traps++;
return (1);
}
int
ctlclrtrap(
sockaddr_u *raddr,
struct interface *linter,
int traptype
)
{
register struct ctl_trap *tp;
if ((tp = ctlfindtrap(raddr, linter)) == NULL)
return (0);
if (tp->tr_flags & TRAP_CONFIGURED
&& traptype != TRAP_TYPE_CONFIG)
return (0);
tp->tr_flags = 0;
num_ctl_traps--;
return (1);
}
static struct ctl_trap *
ctlfindtrap(
sockaddr_u *raddr,
struct interface *linter
)
{
register struct ctl_trap *tp;
for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
if ((tp->tr_flags & TRAP_INUSE)
&& (NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr))
&& SOCK_EQ(raddr, &tp->tr_addr)
&& (linter == tp->tr_localaddr) )
return (tp);
}
return (struct ctl_trap *)NULL;
}
void
report_event(
int err,
struct peer *peer,
const char *str
)
{
char statstr[NTP_MAXSTRLEN];
int i;
size_t len;
if (peer == NULL) {
if (ctl_sys_last_event != (u_char)err)
ctl_sys_num_events= 0;
if (ctl_sys_num_events >= CTL_SYS_MAXEVENTS)
return;
ctl_sys_last_event = (u_char)err;
ctl_sys_num_events++;
snprintf(statstr, NTP_MAXSTRLEN,
"0.0.0.0 %04x %02x %s",
ctlsysstatus(), err, eventstr(err));
if (str != NULL) {
len = strlen(statstr);
snprintf(statstr + len, sizeof(statstr) - len,
" %s", str);
}
NLOG(NLOG_SYSEVENT)
msyslog(LOG_INFO, statstr);
} else {
char *src;
u_char errlast;
errlast = (u_char)err & ~PEER_EVENT;
if (peer->last_event == errlast)
peer->num_events = 0;
if (peer->num_events >= CTL_PEER_MAXEVENTS)
return;
peer->last_event = errlast;
peer->num_events++;
if (ISREFCLOCKADR(&peer->srcadr))
src = refnumtoa(&peer->srcadr);
else
src = stoa(&peer->srcadr);
snprintf(statstr, NTP_MAXSTRLEN,
"%s %04x %02x %s", src,
ctlpeerstatus(peer), err, eventstr(err));
if (str != NULL) {
len = strlen(statstr);
snprintf(statstr + len, sizeof(statstr) - len,
" %s", str);
}
NLOG(NLOG_PEEREVENT)
msyslog(LOG_INFO, statstr);
}
record_proto_stats(statstr);
#if DEBUG
if (debug)
printf("event at %lu %s\n", current_time, statstr);
#endif
if (num_ctl_traps <= 0)
return;
res_opcode = CTL_OP_ASYNCMSG;
res_offset = 0;
res_async = 1;
res_authenticate = 0;
datapt = rpkt.data;
dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
if (!(err & PEER_EVENT)) {
rpkt.associd = 0;
rpkt.status = htons(ctlsysstatus());
for (i = 1; i <= CS_MAXCODE; i++) {
#ifdef OPENSSL
if (i > CS_VARLIST)
continue;
#endif
ctl_putsys(i);
}
} else {
NTP_INSIST(peer != NULL);
rpkt.associd = htons(peer->associd);
rpkt.status = htons(ctlpeerstatus(peer));
for (i = 1; i <= CP_MAXCODE; i++) {
#ifdef OPENSSL
if (i > CP_VARLIST)
continue;
#endif
ctl_putpeer(i, peer);
}
#ifdef REFCLOCK
if (err == PEVNT_CLOCK) {
struct refclockstat clock_stat;
struct ctl_var *kv;
clock_stat.kv_list = (struct ctl_var *)0;
refclock_control(&peer->srcadr,
(struct refclockstat *)0, &clock_stat);
ctl_puthex("refclockstatus",
ctlclkstatus(&clock_stat));
for (i = 1; i <= CC_MAXCODE; i++)
ctl_putclock(i, &clock_stat, 0);
for (kv = clock_stat.kv_list; kv &&
!(kv->flags & EOV); kv++)
if (kv->flags & DEF)
ctl_putdata(kv->text,
strlen(kv->text), 0);
free_varlist(clock_stat.kv_list);
}
#endif
}
ctl_flushpkt(0);
}
void
ctl_clr_stats(void)
{
ctltimereset = current_time;
numctlreq = 0;
numctlbadpkts = 0;
numctlresponses = 0;
numctlfrags = 0;
numctlerrors = 0;
numctlfrags = 0;
numctltooshort = 0;
numctlinputresp = 0;
numctlinputfrag = 0;
numctlinputerr = 0;
numctlbadoffset = 0;
numctlbadversion = 0;
numctldatatooshort = 0;
numctlbadop = 0;
numasyncmsgs = 0;
}
static u_long
count_var(
struct ctl_var *k
)
{
register u_long c;
if (!k)
return (0);
c = 0;
while (!(k++->flags & EOV))
c++;
return (c);
}
char *
add_var(
struct ctl_var **kv,
u_long size,
u_short def
)
{
register u_long c;
register struct ctl_var *k;
c = count_var(*kv);
k = *kv;
*kv = (struct ctl_var *)emalloc((c+2)*sizeof(struct ctl_var));
if (k) {
memmove((char *)*kv, (char *)k,
sizeof(struct ctl_var)*c);
free((char *)k);
}
(*kv)[c].code = (u_short) c;
(*kv)[c].text = (char *)emalloc(size);
(*kv)[c].flags = def;
(*kv)[c+1].code = 0;
(*kv)[c+1].text = (char *)0;
(*kv)[c+1].flags = EOV;
return (char *)(*kv)[c].text;
}
void
set_var(
struct ctl_var **kv,
const char *data,
u_long size,
u_short def
)
{
register struct ctl_var *k;
register const char *s;
register const char *t;
char *td;
if (!data || !size)
return;
k = *kv;
if (k != NULL) {
while (!(k->flags & EOV)) {
s = data;
t = k->text;
if (t) {
while (*t != '=' && *s - *t == 0) {
s++;
t++;
}
if (*s == *t && ((*t == '=') || !*t)) {
free((void *)k->text);
td = (char *)emalloc(size);
memmove(td, data, size);
k->text =td;
k->flags = def;
return;
}
} else {
td = (char *)emalloc(size);
memmove(td, data, size);
k->text = td;
k->flags = def;
return;
}
k++;
}
}
td = add_var(kv, size, def);
memmove(td, data, size);
}
void
set_sys_var(
const char *data,
u_long size,
u_short def
)
{
set_var(&ext_sys_var, data, size, def);
}
void
free_varlist(
struct ctl_var *kv
)
{
struct ctl_var *k;
if (kv) {
for (k = kv; !(k->flags & EOV); k++)
free((void *)k->text);
free((void *)kv);
}
}