summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd/ntp_control.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/ntpd/ntp_control.c')
-rw-r--r--contrib/ntp/ntpd/ntp_control.c2652
1 files changed, 2652 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/ntp_control.c b/contrib/ntp/ntpd/ntp_control.c
new file mode 100644
index 0000000..e63765f
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_control.c
@@ -0,0 +1,2652 @@
+/*
+ * ntp_control.c - respond to control messages and send async traps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_control.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+
+struct ctl_proc {
+ short control_code; /* defined request code */
+ u_short flags; /* flags word */
+ void (*handler) P((struct recvbuf *, int)); /* routine to handle request */
+};
+
+/*
+ * Only one flag. Authentication required or not.
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+/*
+ * Request processing routines
+ */
+static void ctl_error P((int));
+static u_short ctlclkstatus P((struct refclockstat *));
+static void ctl_flushpkt P((int));
+static void ctl_putdata P((const char *, unsigned int, int));
+static void ctl_putstr P((const char *, const char *, unsigned int));
+static void ctl_putdbl P((const char *, double));
+static void ctl_putuint P((const char *, u_long));
+static void ctl_puthex P((const char *, u_long));
+static void ctl_putint P((const char *, long));
+static void ctl_putts P((const char *, l_fp *));
+static void ctl_putadr P((const char *, u_int32));
+static void ctl_putid P((const char *, char *));
+static void ctl_putarray P((const char *, double *, int));
+static void ctl_putsys P((int));
+static void ctl_putpeer P((int, struct peer *));
+#ifdef REFCLOCK
+static void ctl_putclock P((int, struct refclockstat *, int));
+#endif /* REFCLOCK */
+static struct ctl_var *ctl_getitem P((struct ctl_var *, char **));
+static u_long count_var P((struct ctl_var *));
+static void control_unspec P((struct recvbuf *, int));
+static void read_status P((struct recvbuf *, int));
+static void read_variables P((struct recvbuf *, int));
+static void write_variables P((struct recvbuf *, int));
+static void read_clock_status P((struct recvbuf *, int));
+static void write_clock_status P((struct recvbuf *, int));
+static void set_trap P((struct recvbuf *, int));
+static void unset_trap P((struct recvbuf *, int));
+static struct ctl_trap *ctlfindtrap P((struct sockaddr_in *, 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 },
+ { NO_REQUEST, 0 }
+};
+
+/*
+ * System variable values. The array can be indexed by
+ * the variable index to find the textual name.
+ */
+static struct ctl_var sys_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CS_LEAP, RW, "leap" }, /* 1 */
+ { CS_STRATUM, RO, "stratum" }, /* 2 */
+ { CS_PRECISION, RO, "precision" }, /* 3 */
+ { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */
+ { CS_ROOTDISPERSION, RO, "rootdispersion" }, /* 5 */
+ { CS_REFID, RO, "refid" }, /* 6 */
+ { CS_REFTIME, RO, "reftime" }, /* 7 */
+ { CS_POLL, RO, "poll" }, /* 8 */
+ { CS_PEERID, RO, "peer" }, /* 9 */
+ { CS_STATE, RO, "state" }, /* 10 */
+ { CS_OFFSET, RO, "phase" }, /* 11 */
+ { CS_DRIFT, RO, "frequency" }, /* 12 */
+ { CS_COMPLIANCE, RO, "jitter" }, /* 13 */
+ { CS_CLOCK, RO, "clock" }, /* 14 */
+ { CS_PROCESSOR, RO, "processor" }, /* 15 */
+ { CS_SYSTEM, RO, "system" }, /* 16 */
+ { CS_STABIL, RO, "stability" }, /* 17 */
+ { CS_VARLIST, RO, "sys_var_list" }, /* 18 */
+ { 0, EOV, "" }
+};
+
+static struct ctl_var *ext_sys_var = (struct ctl_var *)0;
+
+/*
+ * System variables we print by default (in fuzzball order, more-or-less)
+ */
+static u_char def_sys_var[] = {
+ CS_PROCESSOR,
+ CS_SYSTEM,
+ CS_LEAP,
+ CS_STRATUM,
+ CS_PRECISION,
+ CS_ROOTDELAY,
+ CS_ROOTDISPERSION,
+ CS_PEERID,
+ CS_REFID,
+ CS_REFTIME,
+ CS_POLL,
+ CS_CLOCK,
+ CS_STATE,
+ CS_OFFSET,
+ CS_DRIFT,
+ CS_COMPLIANCE,
+ CS_STABIL,
+ 0
+};
+
+
+/*
+ * Peer variable list
+ */
+static struct ctl_var peer_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CP_CONFIG, RO, "config" }, /* 1 */
+ { CP_AUTHENABLE, RO, "authenable" }, /* 2 */
+ { CP_AUTHENTIC, RO, "authentic" }, /* 3 */
+ { CP_SRCADR, RO, "srcadr" }, /* 4 */
+ { CP_SRCPORT, RO, "srcport" }, /* 5 */
+ { CP_DSTADR, RO, "dstadr" }, /* 6 */
+ { CP_DSTPORT, RO, "dstport" }, /* 7 */
+ { CP_LEAP, RO, "leap" }, /* 8 */
+ { CP_HMODE, RO, "hmode" }, /* 9 */
+ { CP_STRATUM, RO, "stratum" }, /* 10 */
+ { CP_PPOLL, RO, "ppoll" }, /* 11 */
+ { CP_HPOLL, RO, "hpoll" }, /* 12 */
+ { CP_PRECISION, RO, "precision" }, /* 13 */
+ { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */
+ { CP_ROOTDISPERSION, RO, "rootdispersion" }, /* 15 */
+ { CP_REFID, RO, "refid" }, /* 16 */
+ { CP_REFTIME, RO, "reftime" }, /* 17 */
+ { CP_ORG, RO, "org" }, /* 18 */
+ { CP_REC, RO, "rec" }, /* 19 */
+ { CP_XMT, RO, "xmt" }, /* 20 */
+ { CP_REACH, RO, "reach" }, /* 21 */
+ { CP_VALID, RO, "valid" }, /* 22 */
+ { CP_TIMER, RO, "timer" }, /* 23 */
+ { CP_DELAY, RO, "delay" }, /* 24 */
+ { CP_OFFSET, RO, "offset" }, /* 25 */
+ { CP_JITTER, RO, "jitter" }, /* 26 */
+ { CP_DISPERSION,RO, "dispersion" }, /* 27 */
+ { CP_KEYID, RO, "keyid" }, /* 28 */
+ { CP_FILTDELAY, RO, "filtdelay=" }, /* 29 */
+ { CP_FILTOFFSET, RO, "filtoffset=" }, /* 30 */
+ { CP_PMODE, RO, "pmode" }, /* 31 */
+ { CP_RECEIVED, RO, "received"}, /* 32 */
+ { CP_SENT, RO, "sent" }, /* 33 */
+ { CP_FILTERROR, RO, "filtdisp=" }, /* 34 */
+ { CP_FLASH, RO, "flash" }, /* 35 */
+ { CP_DISP, PADDING,"" }, /* 36 */
+ { CP_VARLIST, RO, "peer_var_list" }, /* 37 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Peer variables we print by default
+ */
+static u_char def_peer_var[] = {
+ CP_SRCADR,
+ CP_SRCPORT,
+ CP_DSTADR,
+ CP_DSTPORT,
+ CP_KEYID,
+ CP_STRATUM,
+ CP_PRECISION,
+ CP_ROOTDELAY,
+ CP_ROOTDISPERSION,
+ CP_REFID,
+ CP_REFTIME,
+ CP_DELAY,
+ CP_OFFSET,
+ CP_JITTER,
+ CP_DISPERSION,
+ CP_REACH,
+ CP_VALID,
+ CP_HMODE,
+ CP_PMODE,
+ CP_HPOLL,
+ CP_PPOLL,
+ CP_LEAP,
+ CP_FLASH,
+ CP_ORG,
+ CP_REC,
+ CP_XMT,
+ CP_FILTDELAY,
+ CP_FILTOFFSET,
+ CP_FILTERROR,
+ 0
+};
+
+
+#ifdef REFCLOCK
+/*
+ * Clock variable list
+ */
+static struct ctl_var clock_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CC_TYPE, RO, "type" }, /* 1 */
+ { CC_TIMECODE, RO, "timecode" }, /* 2 */
+ { CC_POLL, RO, "poll" }, /* 3 */
+ { CC_NOREPLY, RO, "noreply" }, /* 4 */
+ { CC_BADFORMAT, RO, "badformat" }, /* 5 */
+ { CC_BADDATA, RO, "baddata" }, /* 6 */
+ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */
+ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */
+ { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */
+ { CC_FUDGEVAL2, RO, "refid" }, /* 10 */
+ { CC_FLAGS, RO, "flags" }, /* 11 */
+ { CC_DEVICE, RO, "device" }, /* 12 */
+ { CC_VARLIST, RO, "clock_var_list" },/* 13 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Clock variables printed by default
+ */
+static u_char def_clock_var[] = {
+ CC_DEVICE,
+ CC_TYPE, /* won't be output if device= known */
+ CC_TIMECODE,
+ CC_POLL,
+ CC_NOREPLY,
+ CC_BADFORMAT,
+ CC_BADDATA,
+ CC_FUDGETIME1,
+ CC_FUDGETIME2,
+ CC_FUDGEVAL1,
+ CC_FUDGEVAL2,
+ CC_FLAGS,
+ 0
+};
+#endif
+
+
+/*
+ * System and processor definitions. These will change for the gizmo board.
+ */
+#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 /* HAVE_UNAME */
+
+/*
+ * Trap structures. We only allow a few of these, and send
+ * a copy of each async message to each live one. Traps time
+ * out after an hour, it is up to the trap receipient to
+ * keep resetting it to avoid being timed out.
+ */
+/* ntp_request.c */
+struct ctl_trap ctl_trap[CTL_MAXTRAPS];
+int num_ctl_traps;
+
+/*
+ * Type bits, for ctlsettrap() call.
+ */
+#define TRAP_TYPE_CONFIG 0 /* used by configuration code */
+#define TRAP_TYPE_PRIO 1 /* priority trap */
+#define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */
+
+
+/*
+ * List relating reference clock types to control message time sources.
+ * Index by the reference clock type.
+ * This list will only be used iff the reference clock driver doesn't
+ * set peer->sstclktype to something different than CTL_SST_TS_UNSPEC.
+ */
+static u_char clocktypes[] = {
+ CTL_SST_TS_NTP, /* REFCLK_NONE (0) */
+ CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_TRAK (2) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */
+ CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */
+ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (5) */
+ CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK (6) */
+ CTL_SST_TS_HF, /* REFCLK_CHU (7) */
+ CTL_SST_TS_LF, /* REFCLOCK_PARSE (default) (8) */
+ CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_ARBITER (11) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */
+ CTL_SST_TS_LF, /* REFCLK_MSF_EES (14) */
+ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (15) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACTS (18) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_VME (21) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_PPS (22) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_PTB_ACTS (23) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_USNO (24) */
+ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (25) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_HP (26) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_ARCRON_MSF (27) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_SHM (28) */
+ CTL_SST_TS_UHF, /* REFCLK_PALISADE (29) */
+ CTL_SST_TS_UHF, /* REFCLK_ONCORE (30) */
+ CTL_SST_TS_UHF, /* REFCLK_JUPITER (31) */
+ CTL_SST_TS_LF, /* REFCLK_CHRONOLOG (32) */
+ CTL_SST_TS_LF, /* REFCLK_DUMBCLOCK (32) */
+ CTL_SST_TS_LF, /* REFCLK_ULINK (33) */
+};
+
+
+/*
+ * Keyid used for authenticating write requests.
+ */
+u_long ctl_auth_keyid;
+
+/*
+ * We keep track of the last error reported by the system internally
+ */
+static u_char ctl_sys_last_event;
+static u_char ctl_sys_num_events;
+
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long ctltimereset; /* time stats reset */
+u_long numctlreq; /* number of requests we've received */
+u_long numctlbadpkts; /* number of bad control packets */
+u_long numctlresponses; /* number of resp packets sent with data */
+u_long numctlfrags; /* number of fragments sent */
+u_long numctlerrors; /* number of error responses sent */
+u_long numctltooshort; /* number of too short input packets */
+u_long numctlinputresp; /* number of responses on input */
+u_long numctlinputfrag; /* number of fragments on input */
+u_long numctlinputerr; /* number of input pkts with err bit set */
+u_long numctlbadoffset; /* number of input pkts with nonzero offset */
+u_long numctlbadversion; /* number of input pkts with unknown version */
+u_long numctldatatooshort; /* data too short for count */
+u_long numctlbadop; /* bad op code found in packet */
+u_long numasyncmsgs; /* number of async messages we've sent */
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct ntp_control rpkt;
+static u_char res_version;
+static u_char res_opcode;
+static u_short res_associd;
+static int res_offset;
+static u_char * datapt;
+static u_char * dataend;
+static int datalinelen;
+static int datanotbinflag;
+static struct sockaddr_in *rmt_addr;
+static struct interface *lcl_inter;
+
+static u_char res_authenticate;
+static u_char res_authokay;
+static u_long res_keyid;
+
+#define MAXDATALINELEN (72)
+
+static u_char res_async; /* set to 1 if this is async trap response */
+
+/*
+ * Pointers for saving state when decoding request packets
+ */
+static char *reqpt;
+static char *reqend;
+
+/*
+ * init_control - initialize request data
+ */
+void
+init_control(void)
+{
+ int i;
+
+#ifdef HAVE_UNAME
+ uname(&utsnamebuf);
+#endif /* HAVE_UNAME */
+
+ 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;
+}
+
+
+/*
+ * ctl_error - send an error response for the current request
+ */
+static void
+ctl_error(
+ int errcode
+ )
+{
+#ifdef DEBUG
+ if (debug >= 4)
+ printf("sending control error %d\n", errcode);
+#endif
+ /*
+ * fill in the fields. We assume rpkt.sequence and rpkt.associd
+ * have already been filled in.
+ */
+ 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;
+
+ /*
+ * send packet and bump counters
+ */
+ 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++;
+}
+
+
+/*
+ * process_control - process an incoming control message
+ */
+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;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("in process_control()\n");
+#endif
+
+ /*
+ * Save the addresses for error responses
+ */
+ numctlreq++;
+ rmt_addr = &rbufp->recv_srcadr;
+ lcl_inter = rbufp->dstadr;
+ pkt = (struct ntp_control *)&rbufp->recv_pkt;
+
+ /*
+ * If the length is less than required for the header, or
+ * it is a response or a fragment, ignore this.
+ */
+ if (rbufp->recv_length < CTL_HEADER_LEN
+ || pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR)
+ || pkt->offset != 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("invalid format in control packet\n");
+#endif
+ 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) {
+#ifdef DEBUG
+ if (debug)
+ printf("unknown version %d in control packet\n",
+ res_version);
+#endif
+ numctlbadversion++;
+ return;
+ }
+
+ /*
+ * Pull enough data from the packet to make intelligent responses
+ */
+ 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)htons(pkt->count);
+ datanotbinflag = 0;
+ datalinelen = 0;
+ datapt = rpkt.data;
+ dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
+
+ /*
+ * We're set up now. Make sure we've got at least
+ * enough incoming data space to match the count.
+ */
+ 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;
+#ifdef DEBUG
+ if (debug >= 2 && (rbufp->recv_length & 0x3) != 0)
+ printf("Packet length %d unrounded\n", rbufp->recv_length);
+#endif
+ /* round up proper len to a 8 octet boundary */
+
+ properlen = (properlen + 7) & ~7;
+ maclen = rbufp->recv_length - properlen;
+ if ((rbufp->recv_length & (sizeof(u_long) - 1)) == 0 &&
+ maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN &&
+ sys_authenticate) {
+ res_authenticate = 1;
+ res_keyid = ntohl(*(u_int32 *)((u_char *)pkt + properlen));
+
+#ifdef DEBUG
+ if (debug >= 3)
+ printf(
+ "recv_len %d, properlen %d, wants auth with keyid %ld, MAC length=%d\n",
+ rbufp->recv_length, properlen, res_keyid, maclen);
+#endif
+ if (!authistrusted(res_keyid)) {
+#ifdef DEBUG
+ if (debug >= 2)
+ printf("invalid keyid %lu\n", res_keyid);
+#endif
+ } else if (authdecrypt(res_keyid, (u_int32 *)pkt,
+ rbufp->recv_length - maclen, maclen)) {
+#ifdef DEBUG
+ if (debug >= 3)
+ printf("authenticated okay\n");
+#endif
+ res_authokay = 1;
+ } else {
+#ifdef DEBUG
+ if (debug >= 3)
+ printf("authentication failed\n");
+#endif
+ res_keyid = 0;
+ }
+ }
+
+ /*
+ * Set up translate pointers
+ */
+ reqpt = (char *)pkt->data;
+ reqend = reqpt + req_count;
+
+ /*
+ * Look for the opcode processor
+ */
+ for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) {
+ if (cc->control_code == res_opcode) {
+#ifdef DEBUG
+ if (debug >= 2)
+ printf("opcode %d, found command handler\n",
+ res_opcode);
+#endif
+ if (cc->flags == AUTH && (!res_authokay
+ || res_keyid != ctl_auth_keyid)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ (cc->handler)(rbufp, restrict_mask);
+ return;
+ }
+ }
+
+ /*
+ * Can't find this one, return an error.
+ */
+ numctlbadop++;
+ ctl_error(CERR_BADOP);
+ return;
+}
+
+
+/*
+ * ctlpeerstatus - return a status word for this peer
+ */
+u_short
+ctlpeerstatus(
+ register struct peer *peer
+ )
+{
+ register u_short status;
+
+ status = peer->status;
+ if (peer->flags & FLAG_CONFIG)
+ status |= CTL_PST_CONFIG;
+ if (peer->flags & FLAG_AUTHENABLE)
+ status |= CTL_PST_AUTHENABLE;
+ if (peer->flags & FLAG_AUTHENTIC)
+ status |= CTL_PST_AUTHENTIC;
+ if (peer->reach != 0)
+ status |= CTL_PST_REACH;
+ return (u_short)CTL_PEER_STATUS(status, peer->num_events,
+ peer->last_event);
+}
+
+
+/*
+ * ctlclkstatus - return a status word for this clock
+ */
+static u_short
+ctlclkstatus(
+ struct refclockstat *this_clock
+ )
+{
+ return ((u_short)(this_clock->currentstatus) << 8)
+ | (u_short)(this_clock->lastevent);
+}
+
+
+
+/*
+ * ctlsysstatus - return the system status word
+ */
+u_short
+ctlsysstatus(void)
+{
+ register u_char this_clock;
+
+ this_clock = CTL_SST_TS_UNSPEC;
+ if (sys_peer != 0) {
+ if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) {
+ this_clock = sys_peer->sstclktype;
+ if (pps_control)
+ this_clock |= CTL_SST_TS_PPS;
+ } else {
+ if (sys_peer->refclktype < sizeof(clocktypes))
+ this_clock = clocktypes[sys_peer->refclktype];
+ if (pps_control)
+ this_clock |= CTL_SST_TS_PPS;
+ }
+ }
+ return (u_short)CTL_SYS_STATUS(sys_leap, this_clock,
+ ctl_sys_num_events, ctl_sys_last_event);
+}
+
+
+
+/*
+ * ctl_flushpkt - write out the current packet and prepare
+ * another if necessary.
+ */
+static void
+ctl_flushpkt(
+ int more
+ )
+{
+ int dlen;
+ int sendlen;
+
+ if (!more && datanotbinflag) {
+ /*
+ * Big hack, output a trailing \r\n
+ */
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ }
+ dlen = datapt - (u_char *)rpkt.data;
+ sendlen = dlen + CTL_HEADER_LEN;
+
+ /*
+ * Pad to a multiple of 32 bits
+ */
+ while (sendlen & 0x3) {
+ *datapt++ = '\0';
+ sendlen++;
+ }
+
+ /*
+ * Fill in the packet with the current info
+ */
+ 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;
+ u_long keyid = htonl(res_keyid);
+
+ /*
+ * If we are going to authenticate, then there is
+ * an additional requirement that the MAC begin on
+ * a 64 bit boundary.
+ */
+ 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++;
+ }
+
+ /*
+ * Set us up for another go around.
+ */
+ res_offset += dlen;
+ datapt = (u_char *)rpkt.data;
+}
+
+
+/*
+ * ctl_putdata - write data into the packet, fragmenting and
+ * starting another if this one is full.
+ */
+static void
+ctl_putdata(
+ const char *dp,
+ unsigned int dlen,
+ int bin /* set to 1 when data is binary */
+ )
+{
+ 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++;
+ }
+ }
+ }
+
+ /*
+ * Save room for trailing junk
+ */
+ if (dlen + overhead + datapt > dataend) {
+ /*
+ * Not enough room in this one, flush it out.
+ */
+ ctl_flushpkt(CTL_MORE);
+ }
+
+ memmove((char *)datapt, dp, (unsigned)dlen);
+ datapt += dlen;
+ datalinelen += dlen;
+}
+
+
+/*
+ * ctl_putstr - write a tagged string into the response packet
+ */
+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);
+}
+
+
+/*
+ * ctl_putdbl - write a tagged, signed double into the response packet
+ */
+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);
+}
+
+/*
+ * ctl_putuint - write a tagged unsigned integer into the response
+ */
+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);
+}
+
+
+/*
+ * ctl_puthex - write a tagged unsigned integer, in hex, into the response
+ */
+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);
+}
+
+
+/*
+ * ctl_putint - write a tagged signed integer into the response
+ */
+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);
+}
+
+
+/*
+ * ctl_putts - write a tagged timestamp, in hex, into the response
+ */
+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 & 0xffffffffL,
+ ts->l_uf & 0xffffffffL);
+ while (*cp != '\0')
+ cp++;
+
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putadr - write a dotted quad IP address into the response
+ */
+static void
+ctl_putadr(
+ const char *tag,
+ u_int32 addr
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = numtoa(addr);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putid - write a tagged clock ID into the response
+ */
+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);
+}
+
+
+/*
+ * ctl_putarray - write a tagged eight element double array into the response
+ */
+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);
+}
+
+
+/*
+ * ctl_putsys - output a system variable
+ */
+static void
+ctl_putsys(
+ int varid
+ )
+{
+ l_fp tmp;
+#ifdef HAVE_UNAME
+ char str[50];
+#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_rootdispersion * 1e3);
+ break;
+ case CS_REFID:
+ if (sys_stratum > 1)
+ ctl_putadr(sys_var[CS_REFID].text, sys_refid);
+ 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_STATE:
+ ctl_putuint(sys_var[CS_STATE].text, (unsigned)state);
+ 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_COMPLIANCE:
+ ctl_putdbl(sys_var[CS_COMPLIANCE].text, sys_error * 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 /* HAVE_UNAME */
+ break;
+ case CS_SYSTEM:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_SYSTEM].text, str_system,
+ sizeof(str_system) - 1);
+#else
+ (void)strcpy(str, utsnamebuf.sysname);
+ (void)strcat(str, utsnamebuf.release);
+ ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str));
+#endif /* HAVE_UNAME */
+ 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) - strlen(sys_var[CS_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name 8-( - Killer */
+
+ strcpy(s, sys_var[CS_VARLIST].text);
+ strcat(s, "=\"");
+ 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++ = ',';
+ strcpy(s, k->text);
+ 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++ = ',';
+ strncpy(s, k->text, (unsigned)i);
+ s += i;
+ }
+
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, (unsigned)( s - buf ), 0);
+ }
+ break;
+ }
+}
+
+
+/*
+ * ctl_putpeer - output a peer variable
+ */
+static void
+ctl_putpeer(
+ int varid,
+ struct peer *peer
+ )
+{
+ switch (varid) {
+ case CP_CONFIG:
+ ctl_putuint(peer_var[CP_CONFIG].text,
+ (unsigned)((peer->flags & FLAG_CONFIG) != 0));
+ break;
+ case CP_AUTHENABLE:
+ ctl_putuint(peer_var[CP_AUTHENABLE].text,
+ (unsigned)((peer->flags & FLAG_AUTHENABLE) != 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,
+ peer->srcadr.sin_addr.s_addr);
+ break;
+ case CP_SRCPORT:
+ ctl_putuint(peer_var[CP_SRCPORT].text,
+ ntohs(peer->srcadr.sin_port));
+ break;
+ case CP_DSTADR:
+ ctl_putadr(peer_var[CP_DSTADR].text,
+ peer->processed ?
+ peer->cast_flags & MDF_BCAST ?
+ peer->dstadr->bcast.sin_addr.s_addr:
+ peer->cast_flags ?
+ peer->dstadr->sin.sin_addr.s_addr ?
+ peer->dstadr->sin.sin_addr.s_addr:
+ peer->dstadr->bcast.sin_addr.s_addr:
+ 8 : 12);
+ break;
+ case CP_DSTPORT:
+ ctl_putuint(peer_var[CP_DSTPORT].text,
+ (u_long)(peer->dstadr
+ ? ntohs(peer->dstadr->sin.sin_port)
+ : 0
+ )
+ );
+ 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->rootdispersion * 1e3);
+ break;
+ case CP_REFID:
+ if (peer->stratum > 1)
+ {
+ if (peer->flags & FLAG_REFCLOCK)
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->srcadr.sin_addr.s_addr);
+ else
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->refid);
+ }
+ 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->org);
+ break;
+ case CP_REC:
+ ctl_putts(peer_var[CP_REC].text, &peer->rec);
+ break;
+ case CP_XMT:
+ ctl_putts(peer_var[CP_XMT].text, &peer->xmt);
+ break;
+ case CP_REACH:
+ ctl_puthex(peer_var[CP_REACH].text, peer->reach);
+ break;
+ case CP_FLASH:
+ ctl_puthex(peer_var[CP_FLASH].text, peer->flash);
+ break;
+ case CP_VALID:
+ ctl_putuint(peer_var[CP_VALID].text, peer->valid);
+ 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,
+ SQRT(peer->variance) * 1e3);
+ break;
+ case CP_DISPERSION:
+ ctl_putdbl(peer_var[CP_DISPERSION].text, peer->disp * 1e3);
+ break;
+ case CP_KEYID:
+ 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) - strlen(peer_var[CP_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name 8-( - Killer */
+
+ strcpy(s, peer_var[CP_VARLIST].text);
+ strcat(s, "=\"");
+ 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++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, (unsigned)(s - buf), 0);
+ }
+ break;
+ }
+}
+
+
+#ifdef REFCLOCK
+/*
+ * ctl_putclock - output clock variables
+ */
+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);
+ 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; /* really long var name 8-( - Killer */
+
+ strcpy(s, clock_var[CC_VARLIST].text);
+ strcat(s, "=\"");
+ 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++ = ',';
+ strcpy(s, k->text);
+ 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++ = ',';
+ strncpy(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
+
+
+
+/*
+ * ctl_getitem - get the next data item from the incoming packet
+ */
+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];
+
+ /*
+ * Delete leading commas and white space
+ */
+ while (reqpt < reqend && (*reqpt == ',' || isspace((int)*reqpt))) {
+ reqpt++;
+ }
+
+ if (reqpt >= reqend)
+ return 0;
+
+ if (var_list == (struct ctl_var *)0)
+ return &eol;
+
+ /*
+ * Look for a first character match on the tag. If we find
+ * one, see if it is a full match.
+ */
+ 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((int)*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((int)*cp))
+ cp++;
+ while (cp < reqend && *cp != ',')
+ *tp++ = *cp++;
+ if (cp < reqend)
+ cp++;
+ *tp = '\0';
+ while (isspace((int)(*(tp-1))))
+ *(--tp) = '\0';
+ reqpt = cp;
+ *data = buf;
+ return v;
+ }
+ }
+ cp = reqpt;
+ }
+ v++;
+ }
+ return v;
+}
+
+
+/*
+ * control_unspec - response to an unspecified op-code
+ */
+/*ARGSUSED*/
+static void
+control_unspec(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ struct peer *peer;
+
+ /*
+ * What is an appropriate response to an unspecified op-code?
+ * I return no errors and no data, unless a specified assocation
+ * doesn't exist.
+ */
+ if (res_associd != 0) {
+ if ((peer = findpeerbyassoc((int)res_associd)) == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ } else {
+ rpkt.status = htons(ctlsysstatus());
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_status - return either a list of associd's, or a particular
+ * peer's status.
+ */
+/*ARGSUSED*/
+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
+ /*
+ * Two choices here. If the specified association ID is
+ * zero we return all known assocation ID's. Otherwise
+ * we return a bunch of stuff about the particular peer.
+ */
+ if (res_associd == 0) {
+ register int n;
+
+ n = 0;
+ rpkt.status = htons(ctlsysstatus());
+ for (i = 0; i < 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((int)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 now, output everything we know about the peer.
+ * May be more selective later.
+ */
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ ctl_flushpkt(0);
+ }
+ }
+}
+
+
+/*
+ * read_variables - return the variables the caller asks for
+ */
+/*ARGSUSED*/
+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) {
+ /*
+ * Wants system variables. Figure out which he wants
+ * and give them to him.
+ */
+ 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; /* shouldn't happen ! */
+ }
+ }
+ 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;
+
+ /*
+ * Wants info for a particular peer. See if we know
+ * the guy.
+ */
+ peer = findpeerbyassoc((int)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);
+}
+
+
+/*
+ * write_variables - write into variables. We only allow leap bit writing
+ * this way.
+ */
+/*ARGSUSED*/
+static void
+write_variables(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ register struct ctl_var *v;
+ register int ext_var;
+ char *valuep;
+ long val;
+ /*int leapind, leapwarn;*/
+
+ /*
+ * If he's trying to write into a peer tell him no way
+ */
+ if (res_associd != 0) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Set status
+ */
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * Set flags to not-in-sync so we can tell when we get something.
+ */
+ /*
+ leapind = ~0;
+ leapwarn = ~0;
+ */
+
+ /*
+ * Look through the variables. Dump out at the first sign of trouble.
+ */
+ 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 {
+ /*
+ * This one seems sane. Save it.
+ */
+ switch(v->code) {
+ case CS_LEAP:
+ default:
+ ctl_error(CERR_UNSPEC); /* our fault, really */
+ return;
+ }
+ }
+ }
+
+ /*
+ * If we got anything, do it.
+ */
+ /*
+ if (leapind != ~0 || leapwarn != ~0) {
+ if (!leap_setleap((int)leapind, (int)leapwarn)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ }
+ */
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_clock_status - return clock radio status
+ */
+/*ARGSUSED*/
+static void
+read_clock_status(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+#ifndef REFCLOCK
+ /*
+ * If no refclock support, no data to return
+ */
+ 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) {
+ /*
+ * Find a clock for this jerk. If the system peer
+ * is a clock use it, else search the hash tables
+ * for one.
+ */
+ if (sys_peer != 0 && (sys_peer->flags & FLAG_REFCLOCK)) {
+ peer = sys_peer;
+ } else {
+ peer = 0;
+ for (i = 0; peer == 0 && i < 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((int)res_associd);
+ if (peer == 0 || !(peer->flags & FLAG_REFCLOCK)) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ }
+
+ /*
+ * If we got here we have a peer which is a clock. Get his status.
+ */
+ clock_stat.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock_stat);
+
+ /*
+ * Look for variables in the packet.
+ */
+ 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; /* shouldn't happen ! */
+ }
+ }
+ 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
+}
+
+
+/*
+ * write_clock_status - we don't do this
+ */
+/*ARGSUSED*/
+static void
+write_clock_status(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ ctl_error(CERR_PERMISSION);
+}
+
+/*
+ * Trap support from here on down. We send async trap messages when the
+ * upper levels report trouble. Traps can by set either by control
+ * messages or by configuration.
+ */
+
+/*
+ * set_trap - set a trap in response to a control message
+ */
+static void
+set_trap(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ int traptype;
+
+ /*
+ * See if this guy is allowed
+ */
+ if (restrict_mask & RES_NOTRAP) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Determine his allowed trap type.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict_mask & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlsettrap() to do the work. Return
+ * an error if it can't assign the trap.
+ */
+ if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype,
+ (int)res_version))
+ ctl_error(CERR_NORESOURCE);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * unset_trap - unset a trap in response to a control message
+ */
+static void
+unset_trap(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ int traptype;
+
+ /*
+ * We don't prevent anyone from removing his own
+ * trap unless the trap is configured. Note we also
+ * must be aware of the possibility that restriction
+ * flags were changed since this guy last set his trap.
+ * Set the trap type based on this.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict_mask & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlclrtrap() to clear this out.
+ */
+ if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype))
+ ctl_error(CERR_BADASSOC);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctlsettrap - called to set a trap
+ */
+int
+ctlsettrap(
+ struct sockaddr_in *raddr,
+ struct interface *linter,
+ int traptype,
+ int version
+ )
+{
+ register struct ctl_trap *tp;
+ register struct ctl_trap *tptouse;
+
+ /*
+ * See if we can find this trap. If so, we only need update
+ * the flags and the time.
+ */
+ 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; /* don't change anything */
+ tp->tr_flags = TRAP_INUSE;
+ break;
+ case TRAP_TYPE_NONPRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return 1; /* don't change anything */
+ tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO;
+ break;
+ }
+ tp->tr_settime = current_time;
+ tp->tr_resets++;
+ return 1;
+ }
+
+ /*
+ * First we heard of this guy. Try to find a trap structure
+ * for him to use, clearing out lesser priority guys if we
+ * have to. Clear out anyone who's expired while we're at it.
+ */
+ 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 we don't have room for him return an error.
+ */
+ if (tptouse == NULL)
+ return 0;
+
+ /*
+ * Set up this structure for him.
+ */
+ 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 = 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;
+}
+
+
+/*
+ * ctlclrtrap - called to clr a trap
+ */
+int
+ctlclrtrap(
+ struct sockaddr_in *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;
+}
+
+
+/*
+ * ctlfindtrap - find a trap given the remote and local addresses
+ */
+static struct ctl_trap *
+ctlfindtrap(
+ struct sockaddr_in *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
+ && NSRCADR(raddr) == NSRCADR(&tp->tr_addr)
+ && NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr)
+ && linter == tp->tr_localaddr)
+ return tp;
+ }
+ return (struct ctl_trap *)NULL;
+}
+
+
+/*
+ * report_event - report an event to the trappers
+ */
+void
+report_event(
+ int err,
+ struct peer *peer
+ )
+{
+ register int i;
+
+ /*
+ * Record error code in proper spots, but have mercy on the
+ * log file.
+ */
+ if (!(err & PEER_EVENT)) {
+ if (ctl_sys_num_events < CTL_SYS_MAXEVENTS)
+ ctl_sys_num_events++;
+ if (ctl_sys_last_event != (u_char)err) {
+ NLOG(NLOG_SYSEVENT)
+ msyslog(LOG_INFO, "system event '%s' (0x%02x) status '%s' (0x%02x)",
+ eventstr(err), err,
+ sysstatstr(ctlsysstatus()), ctlsysstatus());
+#ifdef DEBUG
+ if (debug)
+ printf("report_event: system event '%s' (0x%02x) status '%s' (0x%02x)\n",
+ eventstr(err), err,
+ sysstatstr(ctlsysstatus()), ctlsysstatus());
+#endif
+ ctl_sys_last_event = (u_char)err;
+ }
+ } else if (peer != 0) {
+ char *src;
+
+#ifdef REFCLOCK
+ if (ISREFCLOCKADR(&peer->srcadr))
+ src = refnumtoa(peer->srcadr.sin_addr.s_addr);
+ else
+#endif
+ src = ntoa(&peer->srcadr);
+
+ peer->last_event = (u_char)(err & ~PEER_EVENT);
+ if (peer->num_events < CTL_PEER_MAXEVENTS)
+ peer->num_events++;
+ NLOG(NLOG_PEEREVENT)
+ msyslog(LOG_INFO, "peer %s event '%s' (0x%02x) status '%s' (0x%02x)",
+ src, eventstr(err), err,
+ peerstatstr(ctlpeerstatus(peer)), ctlpeerstatus(peer));
+#ifdef DEBUG
+ if (debug)
+ printf( "peer %s event '%s' (0x%02x) status '%s' (0x%02x)\n",
+ src, eventstr(err), err,
+ peerstatstr(ctlpeerstatus(peer)), ctlpeerstatus(peer));
+#endif
+ } else {
+ msyslog(LOG_ERR, "report_event: err '%s' (0x%02x), no peer", eventstr(err), err);
+#ifdef DEBUG
+ printf("report_event: peer event '%s' (0x%02x), no peer\n", eventstr(err), err);
+#endif
+ return;
+ }
+
+ /*
+ * If no trappers, return.
+ */
+ if (num_ctl_traps <= 0)
+ return;
+
+ /*
+ * Set up the outgoing packet variables
+ */
+ 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 now, put everything we know about system
+ * variables. Maybe more selective later
+ */
+ for (i = 1; i <= CS_MAXCODE; i++)
+ ctl_putsys(i);
+#ifdef REFCLOCK
+ /*
+ * for clock exception events:
+ * add clock variables to reflect info on exception
+ */
+ if (err == EVNT_CLOCKEXCPT) {
+ 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 /*REFCLOCK*/
+ } else {
+ rpkt.associd = htons(peer->associd);
+ rpkt.status = htons(ctlpeerstatus(peer));
+
+ /*
+ * Dump it all. Later, maybe less.
+ */
+ for (i = 1; i <= CP_MAXCODE; i++)
+ ctl_putpeer(i, peer);
+#ifdef REFCLOCK
+ /*
+ * for clock exception events:
+ * add clock variables to reflect info on exception
+ */
+ if (err == EVNT_PEERCLOCK) {
+ 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 /*REFCLOCK*/
+ }
+
+ /*
+ * We're done, return.
+ */
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctl_clr_stats - clear stat counters
+ */
+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,
+ int 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,
+ int def
+ )
+{
+ register struct ctl_var *k;
+ register const char *s;
+ register const char *t;
+ char *td;
+
+ if (!data || !size)
+ return;
+
+ if ((k = *kv))
+ {
+ 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(
+ char *data,
+ u_long size,
+ int 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);
+ }
+}
OpenPOWER on IntegriCloud