summaryrefslogtreecommitdiffstats
path: root/cmdparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmdparse.c')
-rw-r--r--cmdparse.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/cmdparse.c b/cmdparse.c
new file mode 100644
index 0000000..ac5ace2
--- /dev/null
+++ b/cmdparse.c
@@ -0,0 +1,428 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2003
+ * Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Module for parsing various forms of directive and command lines that
+ are common to the configuration file and to the command client.
+
+ */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "cmdparse.h"
+#include "memory.h"
+#include "nameserv.h"
+#include "ntp.h"
+#include "util.h"
+
+/* ================================================== */
+
+int
+CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
+{
+ char *hostname, *cmd;
+ uint32_t ef_type;
+ int n, sel_option;
+
+ src->port = SRC_DEFAULT_PORT;
+ src->params.minpoll = SRC_DEFAULT_MINPOLL;
+ src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
+ src->params.connectivity = SRC_ONLINE;
+ src->params.auto_offline = 0;
+ src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
+ src->params.burst = 0;
+ src->params.iburst = 0;
+ src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
+ src->params.poll_target = SRC_DEFAULT_POLLTARGET;
+ src->params.version = 0;
+ src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
+ src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
+ src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
+ src->params.filter_length = 0;
+ src->params.interleaved = 0;
+ src->params.sel_options = 0;
+ src->params.nts = 0;
+ src->params.nts_port = SRC_DEFAULT_NTSPORT;
+ src->params.copy = 0;
+ src->params.ext_fields = 0;
+ src->params.authkey = INACTIVE_AUTHKEY;
+ src->params.cert_set = SRC_DEFAULT_CERTSET;
+ src->params.max_delay = SRC_DEFAULT_MAXDELAY;
+ src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
+ src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
+ src->params.max_delay_quant = 0.0;
+ src->params.min_delay = 0.0;
+ src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
+ src->params.offset = 0.0;
+
+ hostname = line;
+ line = CPS_SplitWord(line);
+
+ if (!*hostname)
+ return 0;
+
+ src->name = hostname;
+
+ /* Parse options */
+ for (; *line; line += n) {
+ cmd = line;
+ line = CPS_SplitWord(line);
+ n = 0;
+
+ if (!strcasecmp(cmd, "auto_offline")) {
+ src->params.auto_offline = 1;
+ } else if (!strcasecmp(cmd, "burst")) {
+ src->params.burst = 1;
+ } else if (!strcasecmp(cmd, "copy")) {
+ src->params.copy = 1;
+ } else if (!strcasecmp(cmd, "iburst")) {
+ src->params.iburst = 1;
+ } else if (!strcasecmp(cmd, "offline")) {
+ src->params.connectivity = SRC_OFFLINE;
+ } else if (!strcasecmp(cmd, "certset")) {
+ if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "key")) {
+ if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
+ src->params.authkey == INACTIVE_AUTHKEY)
+ return 0;
+ } else if (!strcasecmp(cmd, "asymmetry")) {
+ if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "extfield")) {
+ if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
+ return 0;
+ switch (ef_type) {
+ case NTP_EF_EXP_MONO_ROOT:
+ src->params.ext_fields |= NTP_EF_FLAG_EXP_MONO_ROOT;
+ break;
+ case NTP_EF_EXP_NET_CORRECTION:
+ src->params.ext_fields |= NTP_EF_FLAG_EXP_NET_CORRECTION;
+ break;
+ default:
+ return 0;
+ }
+ } else if (!strcasecmp(cmd, "filter")) {
+ if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxdelay")) {
+ if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxdelayratio")) {
+ if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxdelaydevratio")) {
+ if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxdelayquant")) {
+ if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxpoll")) {
+ if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxsamples")) {
+ if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxsources")) {
+ if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "mindelay")) {
+ if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "minpoll")) {
+ if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "minsamples")) {
+ if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "minstratum")) {
+ if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "nts")) {
+ src->params.nts = 1;
+ } else if (!strcasecmp(cmd, "ntsport")) {
+ if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "offset")) {
+ if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "port")) {
+ if (sscanf(line, "%d%n", &src->port, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "polltarget")) {
+ if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "presend")) {
+ if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "version")) {
+ if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "xleave")) {
+ src->params.interleaved = 1;
+ } else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
+ src->params.sel_options |= sel_option;
+ } else {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* ================================================== */
+
+int
+CPS_GetSelectOption(char *option)
+{
+ if (!strcasecmp(option, "noselect")) {
+ return SRC_SELECT_NOSELECT;
+ } else if (!strcasecmp(option, "prefer")) {
+ return SRC_SELECT_PREFER;
+ } else if (!strcasecmp(option, "require")) {
+ return SRC_SELECT_REQUIRE;
+ } else if (!strcasecmp(option, "trust")) {
+ return SRC_SELECT_TRUST;
+ }
+ return 0;
+}
+
+/* ================================================== */
+
+int
+CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
+{
+ char *p, *net, *slash;
+ uint32_t a, b, c;
+ int bits, len, n;
+
+ p = CPS_SplitWord(line);
+
+ if (strcmp(line, "all") == 0) {
+ *all = 1;
+ net = p;
+ p = CPS_SplitWord(p);
+ } else {
+ *all = 0;
+ net = line;
+ }
+
+ /* Make sure there are no other arguments */
+ if (*p)
+ return 0;
+
+ /* No specified address or network means all IPv4 and IPv6 addresses */
+ if (!*net) {
+ ip->family = IPADDR_UNSPEC;
+ *subnet_bits = 0;
+ return 1;
+ }
+
+ slash = strchr(net, '/');
+ if (slash) {
+ if (sscanf(slash + 1, "%d%n", &bits, &len) != 1 || slash[len + 1] || bits < 0)
+ return 0;
+ *slash = '\0';
+ } else {
+ bits = -1;
+ }
+
+ if (UTI_StringToIP(net, ip)) {
+ if (bits >= 0)
+ *subnet_bits = bits;
+ else
+ *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
+ return 1;
+ }
+
+ /* Check for a shortened IPv4 network notation using only 1, 2, or 3 decimal
+ numbers. This is different than the numbers-and-dots notation accepted
+ by inet_aton()! */
+
+ a = b = c = 0;
+ n = sscanf(net, "%"PRIu32"%n.%"PRIu32"%n.%"PRIu32"%n", &a, &len, &b, &len, &c, &len);
+
+ if (n > 0 && !net[len]) {
+ if (a > 255 || b > 255 || c > 255)
+ return 0;
+
+ ip->family = IPADDR_INET4;
+ ip->addr.in4 = (a << 24) | (b << 16) | (c << 8);
+
+ if (bits >= 0)
+ *subnet_bits = bits;
+ else
+ *subnet_bits = n * 8;
+
+ return 1;
+ }
+
+ /* The last possibility is a hostname */
+ if (bits < 0 && DNS_Name2IPAddress(net, ip, 1) == DNS_Success) {
+ *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* ================================================== */
+
+int
+CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
+{
+ int n;
+ char *cmd;
+
+ *stratum = 10;
+ *distance = 1.0;
+ *orphan = 0;
+
+ while (*line) {
+ cmd = line;
+ line = CPS_SplitWord(line);
+
+ if (!strcasecmp(cmd, "stratum")) {
+ if (sscanf(line, "%d%n", stratum, &n) != 1 ||
+ *stratum >= NTP_MAX_STRATUM || *stratum <= 0)
+ return 0;
+ } else if (!strcasecmp(cmd, "orphan")) {
+ *orphan = 1;
+ n = 0;
+ } else if (!strcasecmp(cmd, "distance")) {
+ if (sscanf(line, "%lf%n", distance, &n) != 1)
+ return 0;
+ } else {
+ return 0;
+ }
+
+ line += n;
+ }
+
+ return 1;
+}
+
+/* ================================================== */
+
+void
+CPS_NormalizeLine(char *line)
+{
+ char *p, *q;
+ int space = 1, first = 1;
+
+ /* Remove white-space at beginning and replace white-spaces with space char */
+ for (p = q = line; *p; p++) {
+ if (isspace((unsigned char)*p)) {
+ if (!space)
+ *q++ = ' ';
+ space = 1;
+ continue;
+ }
+
+ /* Discard comment lines */
+ if (first && strchr("!;#%", *p))
+ break;
+
+ *q++ = *p;
+ space = first = 0;
+ }
+
+ /* Strip trailing space */
+ if (q > line && q[-1] == ' ')
+ q--;
+
+ *q = '\0';
+}
+
+/* ================================================== */
+
+char *
+CPS_SplitWord(char *line)
+{
+ char *p = line, *q = line;
+
+ /* Skip white-space before the word */
+ while (*q && isspace((unsigned char)*q))
+ q++;
+
+ /* Move the word to the beginning */
+ while (*q && !isspace((unsigned char)*q))
+ *p++ = *q++;
+
+ /* Find the next word */
+ while (*q && isspace((unsigned char)*q))
+ q++;
+
+ *p = '\0';
+
+ /* Return pointer to the next word or NUL */
+ return q;
+}
+
+/* ================================================== */
+
+int
+CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
+{
+ char *s1, *s2, *s3, *s4;
+
+ s1 = line;
+ s2 = CPS_SplitWord(s1);
+ s3 = CPS_SplitWord(s2);
+ s4 = CPS_SplitWord(s3);
+
+ /* Require two or three words */
+ if (!*s2 || *s4)
+ return 0;
+
+ if (sscanf(s1, "%"SCNu32, id) != 1)
+ return 0;
+
+ if (*s3) {
+ *type = s2;
+ *key = s3;
+ } else {
+ *type = "MD5";
+ *key = s2;
+ }
+
+ return 1;
+}
+
+/* ================================================== */
+
+int
+CPS_ParseRefid(char *line, uint32_t *ref_id)
+{
+ int i;
+
+ for (i = *ref_id = 0; line[i] && !isspace((unsigned char)line[i]); i++) {
+ if (i >= 4)
+ return 0;
+ *ref_id |= (uint32_t)line[i] << (24 - i * 8);
+ }
+
+ return i;
+}