From 104f986b0650b8f93540785d2bcf486905e49b62 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 5 May 2024 20:43:21 +0200 Subject: Adding upstream version 3.4. Signed-off-by: Daniel Baumann --- client.c | 3290 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3290 insertions(+) create mode 100644 client.c (limited to 'client.c') diff --git a/client.c b/client.c new file mode 100644 index 0000000..029860c --- /dev/null +++ b/client.c @@ -0,0 +1,3290 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2003 + * Copyright (C) Lonnie Abelbeck 2016, 2018 + * Copyright (C) Miroslav Lichvar 2009-2018 + * + * 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. + * + ********************************************************************** + + ======================================================================= + + Command line client for configuring the daemon and obtaining status + from it whilst running. + */ + +#include "config.h" + +#include "sysincl.h" + +#include "array.h" +#include "candm.h" +#include "logging.h" +#include "memory.h" +#include "nameserv.h" +#include "getdate.h" +#include "cmdparse.h" +#include "pktlength.h" +#include "util.h" + +#ifdef FEAT_READLINE +#ifdef USE_EDITLINE +#include +#else +#include +#include +#endif +#endif + +/* ================================================== */ + +union sockaddr_all { + struct sockaddr_in in4; +#ifdef FEAT_IPV6 + struct sockaddr_in6 in6; +#endif + struct sockaddr_un un; + struct sockaddr sa; +}; + +static ARR_Instance sockaddrs; + +static int sock_fd = -1; + +static int quit = 0; + +static int on_terminal = 0; + +static int no_dns = 0; + +static int csv_mode = 0; + +/* ================================================== */ +/* Log a message. This is a minimalistic replacement of the logging.c + implementation to avoid linking with it and other modules. */ + +int log_debug_enabled = 0; + +void LOG_Message(LOG_Severity severity, +#if DEBUG > 0 + int line_number, const char *filename, const char *function_name, +#endif + const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + putc('\n', stderr); + va_end(ap); +} + +/* ================================================== */ +/* Read a single line of commands from standard input */ + +#ifdef FEAT_READLINE +static char **command_name_completion(const char *text, int start, int end); +#endif + +static char * +read_line(void) +{ + static char line[2048]; + static const char *prompt = "chronyc> "; + + if (on_terminal) { +#ifdef FEAT_READLINE + char *cmd; + + rl_attempted_completion_function = command_name_completion; + rl_basic_word_break_characters = " \t\n\r"; + + /* save line only if not empty */ + cmd = readline(prompt); + if( cmd == NULL ) return( NULL ); + + /* user pressed return */ + if( *cmd != '\0' ) { + strncpy(line, cmd, sizeof(line) - 1); + line[sizeof(line) - 1] = '\0'; + add_history(cmd); + /* free the buffer allocated by readline */ + Free(cmd); + } else { + /* simulate the user has entered an empty line */ + *line = '\0'; + } + return( line ); +#else + printf("%s", prompt); + fflush(stdout); +#endif + } + if (fgets(line, sizeof(line), stdin)) { + return line; + } else { + return NULL; + } + +} + +/* ================================================== */ + +static ARR_Instance +get_sockaddrs(const char *hostnames, int port) +{ + ARR_Instance addrs; + char *hostname, *s1, *s2; + IPAddr ip_addrs[DNS_MAX_ADDRESSES]; + union sockaddr_all *addr; + int i; + + addrs = ARR_CreateInstance(sizeof (union sockaddr_all)); + s1 = Strdup(hostnames); + + /* Parse the comma-separated list of hostnames */ + for (hostname = s1; hostname && *hostname; hostname = s2) { + s2 = strchr(hostname, ','); + if (s2) + *s2++ = '\0'; + + /* hostname starting with / is considered a path of Unix domain socket */ + if (hostname[0] == '/') { + addr = (union sockaddr_all *)ARR_GetNewElement(addrs); + if (snprintf(addr->un.sun_path, sizeof (addr->un.sun_path), "%s", hostname) >= + sizeof (addr->un.sun_path)) + LOG_FATAL("Unix socket path too long"); + addr->un.sun_family = AF_UNIX; + } else { + if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) { + DEBUG_LOG("Could not get IP address for %s", hostname); + continue; + } + + for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) { + addr = (union sockaddr_all *)ARR_GetNewElement(addrs); + UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr); + DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i])); + } + } + } + + Free(s1); + return addrs; +} + +/* ================================================== */ +/* Initialise the socket used to talk to the daemon */ + +static int +prepare_socket(union sockaddr_all *addr) +{ + socklen_t addr_len; + char *dir; + + switch (addr->sa.sa_family) { + case AF_UNIX: + addr_len = sizeof (addr->un); + break; + case AF_INET: + addr_len = sizeof (addr->in4); + break; +#ifdef FEAT_IPV6 + case AF_INET6: + addr_len = sizeof (addr->in6); + break; +#endif + default: + assert(0); + } + + sock_fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0); + + if (sock_fd < 0) { + DEBUG_LOG("Could not create socket : %s", strerror(errno)); + return 0; + } + + if (addr->sa.sa_family == AF_UNIX) { + struct sockaddr_un sa_un; + + /* Construct path of our socket. Use the same directory as the server + socket and include our process ID to allow multiple chronyc instances + running at the same time. */ + dir = UTI_PathToDir(addr->un.sun_path); + if (snprintf(sa_un.sun_path, sizeof (sa_un.sun_path), + "%s/chronyc.%d.sock", dir, (int)getpid()) >= sizeof (sa_un.sun_path)) + LOG_FATAL("Unix socket path too long"); + Free(dir); + + sa_un.sun_family = AF_UNIX; + unlink(sa_un.sun_path); + + /* Bind the socket to the path */ + if (bind(sock_fd, (struct sockaddr *)&sa_un, sizeof (sa_un)) < 0) { + DEBUG_LOG("Could not bind socket : %s", strerror(errno)); + return 0; + } + + /* Allow server without root privileges to send replies to our socket */ + if (chmod(sa_un.sun_path, 0666) < 0) { + DEBUG_LOG("Could not change socket permissions : %s", strerror(errno)); + return 0; + } + } + + if (connect(sock_fd, &addr->sa, addr_len) < 0) { + DEBUG_LOG("Could not connect socket : %s", strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +static void +close_io(void) +{ + union sockaddr_all addr; + socklen_t addr_len = sizeof (addr); + + if (sock_fd < 0) + return; + + /* Remove our Unix domain socket */ + if (getsockname(sock_fd, &addr.sa, &addr_len) < 0) + LOG_FATAL("getsockname() failed : %s", strerror(errno)); + if (addr_len <= sizeof (addr) && addr_len > sizeof (addr.sa.sa_family) && + addr.sa.sa_family == AF_UNIX) + unlink(addr.un.sun_path); + + close(sock_fd); + sock_fd = -1; +} + +/* ================================================== */ + +static int +open_io(void) +{ + static unsigned int address_index = 0; + union sockaddr_all *addr; + + /* If a socket is already opened, close it and try the next address */ + if (sock_fd >= 0) { + close_io(); + address_index++; + } + + /* Find an address for which a socket can be opened and connected */ + for (; address_index < ARR_GetSize(sockaddrs); address_index++) { + addr = (union sockaddr_all *)ARR_GetElement(sockaddrs, address_index); + DEBUG_LOG("Opening connection to %s", UTI_SockaddrToString(&addr->sa)); + + if (prepare_socket(addr)) + return 1; + + close_io(); + } + + return 0; +} + +/* ================================================== */ + +static void +bits_to_mask(int bits, int family, IPAddr *mask) +{ + int i; + + mask->family = family; + switch (family) { + case IPADDR_INET4: + if (bits > 32 || bits < 0) + bits = 32; + if (bits > 0) { + mask->addr.in4 = -1; + mask->addr.in4 <<= 32 - bits; + } else { + mask->addr.in4 = 0; + } + break; + case IPADDR_INET6: + if (bits > 128 || bits < 0) + bits = 128; + for (i = 0; i < bits / 8; i++) + mask->addr.in6[i] = 0xff; + if (i < 16) + mask->addr.in6[i++] = (0xff << (8 - bits % 8)) & 0xff; + for (; i < 16; i++) + mask->addr.in6[i] = 0x0; + break; + default: + assert(0); + } +} + +/* ================================================== */ + +static int +read_mask_address(char *line, IPAddr *mask, IPAddr *address) +{ + unsigned int bits; + char *p, *q; + + p = line; + if (!*p) { + mask->family = address->family = IPADDR_UNSPEC; + return 1; + } else { + q = strchr(p, '/'); + if (q) { + *q++ = 0; + if (UTI_StringToIP(p, mask)) { + p = q; + if (UTI_StringToIP(p, address)) { + if (address->family == mask->family) + return 1; + } else if (sscanf(p, "%u", &bits) == 1) { + *address = *mask; + bits_to_mask(bits, address->family, mask); + return 1; + } + } + } else { + if (DNS_Name2IPAddress(p, address, 1) == DNS_Success) { + bits_to_mask(-1, address->family, mask); + return 1; + } else { + LOG(LOGS_ERR, "Could not get address for hostname"); + return 0; + } + } + } + + LOG(LOGS_ERR, "Invalid syntax for mask/address"); + return 0; +} + +/* ================================================== */ + +static int +process_cmd_offline(CMD_Request *msg, char *line) +{ + IPAddr mask, address; + int ok; + + if (read_mask_address(line, &mask, &address)) { + UTI_IPHostToNetwork(&mask, &msg->data.offline.mask); + UTI_IPHostToNetwork(&address, &msg->data.offline.address); + msg->command = htons(REQ_OFFLINE); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + + +static int +process_cmd_online(CMD_Request *msg, char *line) +{ + IPAddr mask, address; + int ok; + + if (read_mask_address(line, &mask, &address)) { + UTI_IPHostToNetwork(&mask, &msg->data.online.mask); + UTI_IPHostToNetwork(&address, &msg->data.online.address); + msg->command = htons(REQ_ONLINE); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static void +process_cmd_onoffline(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_ONOFFLINE); +} + +/* ================================================== */ + +static int +read_address_integer(char *line, IPAddr *address, int *value) +{ + char *hostname; + int ok = 0; + + hostname = line; + line = CPS_SplitWord(line); + + if (sscanf(line, "%d", value) != 1) { + LOG(LOGS_ERR, "Invalid syntax for address value"); + ok = 0; + } else { + if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { + LOG(LOGS_ERR, "Could not get address for hostname"); + ok = 0; + } else { + ok = 1; + } + } + + return ok; + +} + + +/* ================================================== */ + +static int +read_address_double(char *line, IPAddr *address, double *value) +{ + char *hostname; + int ok = 0; + + hostname = line; + line = CPS_SplitWord(line); + + if (sscanf(line, "%lf", value) != 1) { + LOG(LOGS_ERR, "Invalid syntax for address value"); + ok = 0; + } else { + if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { + LOG(LOGS_ERR, "Could not get address for hostname"); + ok = 0; + } else { + ok = 1; + } + } + + return ok; + +} + + +/* ================================================== */ + +static int +process_cmd_minpoll(CMD_Request *msg, char *line) +{ + IPAddr address; + int minpoll; + int ok; + + if (read_address_integer(line, &address, &minpoll)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_minpoll.address); + msg->data.modify_minpoll.new_minpoll = htonl(minpoll); + msg->command = htons(REQ_MODIFY_MINPOLL); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxpoll(CMD_Request *msg, char *line) +{ + IPAddr address; + int maxpoll; + int ok; + + if (read_address_integer(line, &address, &maxpoll)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_maxpoll.address); + msg->data.modify_maxpoll.new_maxpoll = htonl(maxpoll); + msg->command = htons(REQ_MODIFY_MAXPOLL); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxdelay(CMD_Request *msg, char *line) +{ + IPAddr address; + double max_delay; + int ok; + + if (read_address_double(line, &address, &max_delay)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelay.address); + msg->data.modify_maxdelay.new_max_delay = UTI_FloatHostToNetwork(max_delay); + msg->command = htons(REQ_MODIFY_MAXDELAY); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxdelaydevratio(CMD_Request *msg, char *line) +{ + IPAddr address; + double max_delay_dev_ratio; + int ok; + + if (read_address_double(line, &address, &max_delay_dev_ratio)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelaydevratio.address); + msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_dev_ratio); + msg->command = htons(REQ_MODIFY_MAXDELAYDEVRATIO); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxdelayratio(CMD_Request *msg, char *line) +{ + IPAddr address; + double max_delay_ratio; + int ok; + + if (read_address_double(line, &address, &max_delay_ratio)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelayratio.address); + msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_ratio); + msg->command = htons(REQ_MODIFY_MAXDELAYRATIO); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_minstratum(CMD_Request *msg, char *line) +{ + IPAddr address; + int min_stratum; + int ok; + + if (read_address_integer(line, &address, &min_stratum)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_minstratum.address); + msg->data.modify_minstratum.new_min_stratum = htonl(min_stratum); + msg->command = htons(REQ_MODIFY_MINSTRATUM); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_polltarget(CMD_Request *msg, char *line) +{ + IPAddr address; + int poll_target; + int ok; + + if (read_address_integer(line, &address, &poll_target)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_polltarget.address); + msg->data.modify_polltarget.new_poll_target = htonl(poll_target); + msg->command = htons(REQ_MODIFY_POLLTARGET); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxupdateskew(CMD_Request *msg, char *line) +{ + int ok; + double new_max_update_skew; + + if (sscanf(line, "%lf", &new_max_update_skew) == 1) { + msg->data.modify_maxupdateskew.new_max_update_skew = UTI_FloatHostToNetwork(new_max_update_skew); + msg->command = htons(REQ_MODIFY_MAXUPDATESKEW); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static void +process_cmd_dump(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_DUMP); + msg->data.dump.pad = htonl(0); +} + +/* ================================================== */ + +static void +process_cmd_writertc(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_WRITERTC); +} + +/* ================================================== */ + +static void +process_cmd_trimrtc(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_TRIMRTC); +} + +/* ================================================== */ + +static void +process_cmd_cyclelogs(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_CYCLELOGS); +} + +/* ================================================== */ + +static int +process_cmd_burst(CMD_Request *msg, char *line) +{ + int n_good_samples, n_total_samples; + char *s1, *s2; + IPAddr address, mask; + + s1 = line; + s2 = CPS_SplitWord(s1); + CPS_SplitWord(s2); + + if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) { + LOG(LOGS_ERR, "Invalid syntax for burst command"); + return 0; + } + + mask.family = address.family = IPADDR_UNSPEC; + if (*s2 && !read_mask_address(s2, &mask, &address)) { + return 0; + } + + msg->command = htons(REQ_BURST); + msg->data.burst.n_good_samples = ntohl(n_good_samples); + msg->data.burst.n_total_samples = ntohl(n_total_samples); + + UTI_IPHostToNetwork(&mask, &msg->data.burst.mask); + UTI_IPHostToNetwork(&address, &msg->data.burst.address); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_local(CMD_Request *msg, char *line) +{ + int on_off, stratum = 0, orphan = 0; + double distance = 0.0; + + if (!strcmp(line, "off")) { + on_off = 0; + } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) { + on_off = 1; + } else { + LOG(LOGS_ERR, "Invalid syntax for local command"); + return 0; + } + + msg->command = htons(REQ_LOCAL2); + msg->data.local.on_off = htonl(on_off); + msg->data.local.stratum = htonl(stratum); + msg->data.local.distance = UTI_FloatHostToNetwork(distance); + msg->data.local.orphan = htonl(orphan); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_manual(CMD_Request *msg, const char *line) +{ + const char *p; + + p = line; + + if (!strcmp(p, "off")) { + msg->data.manual.option = htonl(0); + } else if (!strcmp(p, "on")) { + msg->data.manual.option = htonl(1); + } else if (!strcmp(p, "reset")) { + msg->data.manual.option = htonl(2); + } else { + LOG(LOGS_ERR, "Invalid syntax for manual command"); + return 0; + } + msg->command = htons(REQ_MANUAL); + + return 1; +} + +/* ================================================== */ + +static int +parse_allow_deny(CMD_Request *msg, char *line) +{ + unsigned long a, b, c, d; + int n, specified_subnet_bits; + IPAddr ip; + char *p; + + p = line; + if (!*p) { + /* blank line - applies to all addresses */ + ip.family = IPADDR_UNSPEC; + UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); + msg->data.allow_deny.subnet_bits = htonl(0); + } else { + char *slashpos; + slashpos = strchr(p, '/'); + if (slashpos) *slashpos = 0; + + n = 0; + if (!UTI_StringToIP(p, &ip) && + (n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) <= 0) { + + /* Try to parse as the name of a machine */ + if (slashpos || DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) { + LOG(LOGS_ERR, "Could not read address"); + return 0; + } else { + UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); + if (ip.family == IPADDR_INET6) + msg->data.allow_deny.subnet_bits = htonl(128); + else + msg->data.allow_deny.subnet_bits = htonl(32); + } + } else { + + if (n == 0) { + if (ip.family == IPADDR_INET6) + msg->data.allow_deny.subnet_bits = htonl(128); + else + msg->data.allow_deny.subnet_bits = htonl(32); + } else { + ip.family = IPADDR_INET4; + + a &= 0xff; + b &= 0xff; + c &= 0xff; + d &= 0xff; + + switch (n) { + case 1: + ip.addr.in4 = htonl((a<<24)); + msg->data.allow_deny.subnet_bits = htonl(8); + break; + case 2: + ip.addr.in4 = htonl((a<<24) | (b<<16)); + msg->data.allow_deny.subnet_bits = htonl(16); + break; + case 3: + ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8)); + msg->data.allow_deny.subnet_bits = htonl(24); + break; + case 4: + ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8) | d); + msg->data.allow_deny.subnet_bits = htonl(32); + break; + default: + assert(0); + } + } + + UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); + + if (slashpos) { + n = sscanf(slashpos+1, "%d", &specified_subnet_bits); + if (n == 1) { + msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits); + } else { + LOG(LOGS_WARN, "Warning: badly formatted subnet size, using %d", + (int)ntohl(msg->data.allow_deny.subnet_bits)); + } + } + } + } + return 1; +} + +/* ================================================== */ + +static int +process_cmd_allow(CMD_Request *msg, char *line) +{ + int status; + msg->command = htons(REQ_ALLOW); + status = parse_allow_deny(msg, line); + return status; +} + +/* ================================================== */ + +static int +process_cmd_allowall(CMD_Request *msg, char *line) +{ + int status; + msg->command = htons(REQ_ALLOWALL); + status = parse_allow_deny(msg, line); + return status; +} + +/* ================================================== */ + +static int +process_cmd_deny(CMD_Request *msg, char *line) +{ + int status; + msg->command = htons(REQ_DENY); + status = parse_allow_deny(msg, line); + return status; +} + +/* ================================================== */ + +static int +process_cmd_denyall(CMD_Request *msg, char *line) +{ + int status; + msg->command = htons(REQ_DENYALL); + status = parse_allow_deny(msg, line); + return status; +} + +/* ================================================== */ + +static int +process_cmd_cmdallow(CMD_Request *msg, char *line) +{ + int status; + msg->command = htons(REQ_CMDALLOW); + status = parse_allow_deny(msg, line); + return status; +} + +/* ================================================== */ + +static int +process_cmd_cmdallowall(CMD_Request *msg, char *line) +{ + int status; + msg->command = htons(REQ_CMDALLOWALL); + status = parse_allow_deny(msg, line); + return status; +} + +/* ================================================== */ + +static int +process_cmd_cmddeny(CMD_Request *msg, char *line) +{ + int status; + msg->command = htons(REQ_CMDDENY); + status = parse_allow_deny(msg, line); + return status; +} + +/* ================================================== */ + +static int +process_cmd_cmddenyall(CMD_Request *msg, char *line) +{ + int status; + msg->command = htons(REQ_CMDDENYALL); + status = parse_allow_deny(msg, line); + return status; +} + +/* ================================================== */ + +static int +accheck_getaddr(char *line, IPAddr *addr) +{ + unsigned long a, b, c, d; + IPAddr ip; + char *p; + p = line; + if (!*p) { + return 0; + } else { + if (sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d) == 4) { + addr->family = IPADDR_INET4; + addr->addr.in4 = (a<<24) | (b<<16) | (c<<8) | d; + return 1; + } else { + if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) { + return 0; + } else { + *addr = ip; + return 1; + } + } + } +} + +/* ================================================== */ + +static int +process_cmd_accheck(CMD_Request *msg, char *line) +{ + IPAddr ip; + msg->command = htons(REQ_ACCHECK); + if (accheck_getaddr(line, &ip)) { + UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); + return 1; + } else { + LOG(LOGS_ERR, "Could not read address"); + return 0; + } +} + +/* ================================================== */ + +static int +process_cmd_cmdaccheck(CMD_Request *msg, char *line) +{ + IPAddr ip; + msg->command = htons(REQ_CMDACCHECK); + if (accheck_getaddr(line, &ip)) { + UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); + return 1; + } else { + LOG(LOGS_ERR, "Could not read address"); + return 0; + } +} + +/* ================================================== */ + +static void +process_cmd_dfreq(CMD_Request *msg, char *line) +{ + double dfreq; + msg->command = htons(REQ_DFREQ); + if (sscanf(line, "%lf", &dfreq) == 1) { + msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq); + } else { + msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(0.0); + } +} + +/* ================================================== */ + +static void +cvt_to_sec_usec(double x, long *sec, long *usec) { + long s, us; + s = (long) x; + us = (long)(0.5 + 1.0e6 * (x - (double) s)); + while (us >= 1000000) { + us -= 1000000; + s += 1; + } + while (us < 0) { + us += 1000000; + s -= 1; + } + + *sec = s; + *usec = us; +} + +/* ================================================== */ + +static void +process_cmd_doffset(CMD_Request *msg, char *line) +{ + double doffset; + long sec, usec; + msg->command = htons(REQ_DOFFSET); + if (sscanf(line, "%lf", &doffset) == 1) { + cvt_to_sec_usec(doffset, &sec, &usec); + msg->data.doffset.sec = htonl(sec); + msg->data.doffset.usec = htonl(usec); + } else { + msg->data.doffset.sec = htonl(0); + msg->data.doffset.usec = htonl(0); + } +} + +/* ================================================== */ + +static int +process_cmd_add_server_or_peer(CMD_Request *msg, char *line) +{ + CPS_NTP_Source data; + IPAddr ip_addr; + int result = 0, status; + const char *opt_name; + + status = CPS_ParseNTPSourceAdd(line, &data); + switch (status) { + case 0: + LOG(LOGS_ERR, "Invalid syntax for add command"); + break; + default: + if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) { + LOG(LOGS_ERR, "Invalid host/IP address"); + break; + } + + opt_name = NULL; + if (opt_name) { + LOG(LOGS_ERR, "%s can't be set in chronyc", opt_name); + break; + } + + msg->data.ntp_source.port = htonl((unsigned long) data.port); + UTI_IPHostToNetwork(&ip_addr, &msg->data.ntp_source.ip_addr); + msg->data.ntp_source.minpoll = htonl(data.params.minpoll); + msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll); + msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll); + msg->data.ntp_source.min_stratum = htonl(data.params.min_stratum); + msg->data.ntp_source.poll_target = htonl(data.params.poll_target); + msg->data.ntp_source.version = htonl(data.params.version); + msg->data.ntp_source.max_sources = htonl(data.params.max_sources); + msg->data.ntp_source.min_samples = htonl(data.params.min_samples); + msg->data.ntp_source.max_samples = htonl(data.params.max_samples); + msg->data.ntp_source.authkey = htonl(data.params.authkey); + msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay); + msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio); + msg->data.ntp_source.max_delay_dev_ratio = + UTI_FloatHostToNetwork(data.params.max_delay_dev_ratio); + msg->data.ntp_source.min_delay = UTI_FloatHostToNetwork(data.params.min_delay); + msg->data.ntp_source.asymmetry = UTI_FloatHostToNetwork(data.params.asymmetry); + msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset); + msg->data.ntp_source.flags = htonl( + (data.params.connectivity == SRC_ONLINE ? REQ_ADDSRC_ONLINE : 0) | + (data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) | + (data.params.iburst ? REQ_ADDSRC_IBURST : 0) | + (data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) | + (data.params.burst ? REQ_ADDSRC_BURST : 0) | + (data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) | + (data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) | + (data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) | + (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0)); + msg->data.ntp_source.filter_length = htonl(data.params.filter_length); + memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved)); + + result = 1; + + break; + } + + return result; +} + +/* ================================================== */ + +static int +process_cmd_add_server(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_ADD_SERVER3); + return process_cmd_add_server_or_peer(msg, line); +} + +/* ================================================== */ + +static int +process_cmd_add_peer(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_ADD_PEER3); + return process_cmd_add_server_or_peer(msg, line); +} + +/* ================================================== */ + +static int +process_cmd_delete(CMD_Request *msg, char *line) +{ + char *hostname; + int ok = 0; + IPAddr address; + + msg->command = htons(REQ_DEL_SOURCE); + hostname = line; + CPS_SplitWord(line); + + if (!*hostname) { + LOG(LOGS_ERR, "Invalid syntax for address"); + ok = 0; + } else { + if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) { + LOG(LOGS_ERR, "Could not get address for hostname"); + ok = 0; + } else { + UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr); + ok = 1; + } + } + + return ok; + +} + +/* ================================================== */ + +static void +give_help(void) +{ + int line, len; + const char *s, cols[] = + "System clock:\0\0" + "tracking\0Display system time information\0" + "makestep\0Correct clock by stepping immediately\0" + "makestep \0Configure automatic clock stepping\0" + "maxupdateskew \0Modify maximum valid skew to update frequency\0" + "waitsync [ [ [ []]]]\0" + "Wait until synchronised in specified limits\0" + "\0\0" + "Time sources:\0\0" + "sources [-v]\0Display information about current sources\0" + "sourcestats [-v]\0Display statistics about collected measurements\0" + "reselect\0Force reselecting synchronisation source\0" + "reselectdist \0Modify reselection distance\0" + "\0\0" + "NTP sources:\0\0" + "activity\0Check how many NTP sources are online/offline\0" + "ntpdata [
]\0Display information about last valid measurement\0" + "add server
[options]\0Add new NTP server\0" + "add peer
[options]\0Add new NTP peer\0" + "delete
\0Remove server or peer\0" + "burst / [/
]\0Start rapid set of measurements\0" + "maxdelay
\0Modify maximum valid sample delay\0" + "maxdelayratio
\0Modify maximum valid delay/minimum ratio\0" + "maxdelaydevratio
\0Modify maximum valid delay/deviation ratio\0" + "minpoll
\0Modify minimum polling interval\0" + "maxpoll
\0Modify maximum polling interval\0" + "minstratum
\0Modify minimum stratum\0" + "offline [/
]\0Set sources in subnet to offline status\0" + "online [/
]\0Set sources in subnet to online status\0" + "onoffline\0Set all sources to online or offline status\0" + "\0according to network configuration\0" + "polltarget
\0Modify poll target\0" + "refresh\0Refresh IP addresses\0" + "\0\0" + "Manual time input:\0\0" + "manual off|on|reset\0Disable/enable/reset settime command\0" + "manual list\0Show previous settime entries\0" + "manual delete \0Delete previous settime entry\0" + "settime