1 /*
2   chronyd/chronyc - Programs for keeping computer clocks accurate.
3 
4  **********************************************************************
5  * Copyright (C) Richard P. Curnow  1997-2003
6  * Copyright (C) Lonnie Abelbeck  2016, 2018
7  * Copyright (C) Miroslav Lichvar  2009-2021
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of version 2 of the GNU General Public License as
11  * published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  *
22  **********************************************************************
23 
24   =======================================================================
25 
26   Command line client for configuring the daemon and obtaining status
27   from it whilst running.
28   */
29 
30 #include "config.h"
31 
32 #include "sysincl.h"
33 
34 #include "array.h"
35 #include "candm.h"
36 #include "cmac.h"
37 #include "logging.h"
38 #include "memory.h"
39 #include "nameserv.h"
40 #include "getdate.h"
41 #include "cmdparse.h"
42 #include "pktlength.h"
43 #include "socket.h"
44 #include "util.h"
45 
46 #ifdef FEAT_READLINE
47 #include <editline/readline.h>
48 #endif
49 
50 /* ================================================== */
51 
52 struct Address {
53   SCK_AddressType type;
54   union {
55     IPSockAddr ip;
56     char *path;
57   } addr;
58 };
59 
60 static ARR_Instance server_addresses;
61 
62 static int sock_fd = -1;
63 
64 static volatile int quit = 0;
65 
66 static int on_terminal = 0;
67 
68 static int no_dns = 0;
69 
70 static int source_names = 0;
71 
72 static int csv_mode = 0;
73 
74 /* ================================================== */
75 /* Log a message. This is a minimalistic replacement of the logging.c
76    implementation to avoid linking with it and other modules. */
77 
78 LOG_Severity log_min_severity = LOGS_INFO;
79 
LOG_Message(LOG_Severity severity,int line_number,const char * filename,const char * function_name,const char * format,...)80 void LOG_Message(LOG_Severity severity,
81 #if DEBUG > 0
82                  int line_number, const char *filename, const char *function_name,
83 #endif
84                  const char *format, ...)
85 {
86   va_list ap;
87 
88   if (severity < log_min_severity)
89     return;
90 
91   va_start(ap, format);
92   vfprintf(stderr, format, ap);
93   putc('\n', stderr);
94   va_end(ap);
95 }
96 
97 /* ================================================== */
98 /* Read a single line of commands from standard input */
99 
100 #ifdef FEAT_READLINE
101 static char **command_name_completion(const char *text, int start, int end);
102 #endif
103 
104 static char *
read_line(void)105 read_line(void)
106 {
107   static char line[2048];
108   static const char *prompt = "chronyc> ";
109 
110   if (on_terminal) {
111 #ifdef FEAT_READLINE
112     char *cmd;
113 
114     rl_attempted_completion_function = command_name_completion;
115     rl_basic_word_break_characters = " \t\n\r";
116 
117     /* save line only if not empty */
118     cmd = readline(prompt);
119     if( cmd == NULL ) return( NULL );
120 
121     /* user pressed return */
122     if( *cmd != '\0' ) {
123       strncpy(line, cmd, sizeof(line) - 1);
124       line[sizeof(line) - 1] = '\0';
125       add_history(cmd);
126       /* free the buffer allocated by readline */
127       Free(cmd);
128     } else {
129       /* simulate the user has entered an empty line */
130       *line = '\0';
131     }
132     return( line );
133 #else
134     printf("%s", prompt);
135     fflush(stdout);
136 #endif
137   }
138   if (fgets(line, sizeof(line), stdin)) {
139     return line;
140   } else {
141     return NULL;
142   }
143 
144 }
145 
146 /* ================================================== */
147 
148 static ARR_Instance
get_addresses(const char * hostnames,int port)149 get_addresses(const char *hostnames, int port)
150 {
151   struct Address *addr;
152   ARR_Instance addrs;
153   char *hostname, *s1, *s2;
154   IPAddr ip_addrs[DNS_MAX_ADDRESSES];
155   int i;
156 
157   addrs = ARR_CreateInstance(sizeof (*addr));
158   s1 = Strdup(hostnames);
159 
160   /* Parse the comma-separated list of hostnames */
161   for (hostname = s1; hostname && *hostname; hostname = s2) {
162     s2 = strchr(hostname, ',');
163     if (s2)
164       *s2++ = '\0';
165 
166     /* hostname starting with / is considered a path of Unix domain socket */
167     if (hostname[0] == '/') {
168       addr = ARR_GetNewElement(addrs);
169       addr->type = SCK_ADDR_UNIX;
170       addr->addr.path = Strdup(hostname);
171     } else {
172       if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) {
173         DEBUG_LOG("Could not get IP address for %s", hostname);
174         continue;
175       }
176 
177       for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) {
178         addr = ARR_GetNewElement(addrs);
179         addr->type = SCK_ADDR_IP;
180         addr->addr.ip.ip_addr = ip_addrs[i];
181         addr->addr.ip.port = port;
182         DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
183       }
184     }
185   }
186 
187   Free(s1);
188   return addrs;
189 }
190 
191 /* ================================================== */
192 
193 static void
free_addresses(ARR_Instance addresses)194 free_addresses(ARR_Instance addresses)
195 {
196   struct Address *addr;
197   unsigned int i;
198 
199   for (i = 0; i < ARR_GetSize(addresses); i++) {
200     addr = ARR_GetElement(addresses, i);
201 
202     if (addr->type == SCK_ADDR_UNIX)
203       Free(addr->addr.path);
204   }
205 
206   ARR_DestroyInstance(addresses);
207 }
208 
209 /* ================================================== */
210 /* Initialise the socket used to talk to the daemon */
211 
212 static int
open_socket(struct Address * addr)213 open_socket(struct Address *addr)
214 {
215   char *dir, *local_addr;
216   size_t local_addr_len;
217 
218   switch (addr->type) {
219     case SCK_ADDR_IP:
220       sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, NULL, 0);
221       break;
222     case SCK_ADDR_UNIX:
223       /* Construct path of our socket.  Use the same directory as the server
224          socket and include our process ID to allow multiple chronyc instances
225          running at the same time. */
226 
227       dir = UTI_PathToDir(addr->addr.path);
228       local_addr_len = strlen(dir) + 50;
229       local_addr = Malloc(local_addr_len);
230 
231       snprintf(local_addr, local_addr_len, "%s/chronyc.%d.sock", dir, (int)getpid());
232 
233       sock_fd = SCK_OpenUnixDatagramSocket(addr->addr.path, local_addr,
234                                            SCK_FLAG_ALL_PERMISSIONS);
235       Free(dir);
236       Free(local_addr);
237 
238       break;
239     default:
240       assert(0);
241   }
242 
243   if (sock_fd < 0)
244     return 0;
245 
246   return 1;
247 }
248 
249 /* ================================================== */
250 
251 static void
close_io(void)252 close_io(void)
253 {
254   if (sock_fd < 0)
255     return;
256 
257   SCK_RemoveSocket(sock_fd);
258   SCK_CloseSocket(sock_fd);
259   sock_fd = -1;
260 }
261 
262 /* ================================================== */
263 
264 static int
open_io(void)265 open_io(void)
266 {
267   static unsigned int address_index = 0;
268   struct Address *addr;
269 
270   /* If a socket is already opened, close it and try the next address */
271   if (sock_fd >= 0) {
272     close_io();
273     address_index++;
274   }
275 
276   /* Find an address for which a socket can be opened and connected */
277   for (; address_index < ARR_GetSize(server_addresses); address_index++) {
278     addr = ARR_GetElement(server_addresses, address_index);
279 
280     if (open_socket(addr))
281       return 1;
282 
283     close_io();
284   }
285 
286   return 0;
287 }
288 
289 /* ================================================== */
290 
291 static void
bits_to_mask(int bits,int family,IPAddr * mask)292 bits_to_mask(int bits, int family, IPAddr *mask)
293 {
294   int i;
295 
296   mask->family = family;
297   switch (family) {
298     case IPADDR_INET4:
299       if (bits > 32 || bits < 0)
300         bits = 32;
301       if (bits > 0) {
302         mask->addr.in4 = -1;
303         mask->addr.in4 <<= 32 - bits;
304       } else {
305         mask->addr.in4 = 0;
306       }
307       break;
308     case IPADDR_INET6:
309       if (bits > 128 || bits < 0)
310         bits = 128;
311       for (i = 0; i < bits / 8; i++)
312         mask->addr.in6[i] = 0xff;
313       if (i < 16)
314         mask->addr.in6[i++] = (0xff << (8 - bits % 8)) & 0xff;
315       for (; i < 16; i++)
316         mask->addr.in6[i] = 0x0;
317       break;
318     case IPADDR_ID:
319       mask->family = IPADDR_UNSPEC;
320       break;
321     default:
322       assert(0);
323   }
324 }
325 
326 /* ================================================== */
327 
328 static int
parse_source_address(char * word,IPAddr * address)329 parse_source_address(char *word, IPAddr *address)
330 {
331   if (UTI_StringToIdIP(word, address))
332     return 1;
333 
334   if (DNS_Name2IPAddress(word, address, 1) == DNS_Success)
335     return 1;
336 
337   return 0;
338 }
339 
340 /* ================================================== */
341 
342 static int
read_mask_address(char * line,IPAddr * mask,IPAddr * address)343 read_mask_address(char *line, IPAddr *mask, IPAddr *address)
344 {
345   unsigned int bits;
346   char *p, *q;
347 
348   p = line;
349   if (!*p) {
350     mask->family = address->family = IPADDR_UNSPEC;
351     return 1;
352   } else {
353     q = strchr(p, '/');
354     if (q) {
355       *q++ = 0;
356       if (UTI_StringToIP(p, mask)) {
357         p = q;
358         if (UTI_StringToIP(p, address)) {
359           if (address->family == mask->family)
360             return 1;
361         } else if (sscanf(p, "%u", &bits) == 1) {
362           *address = *mask;
363           bits_to_mask(bits, address->family, mask);
364           return 1;
365         }
366       }
367     } else {
368       if (parse_source_address(p, address)) {
369         bits_to_mask(-1, address->family, mask);
370         return 1;
371       } else {
372         LOG(LOGS_ERR, "Could not get address for hostname");
373         return 0;
374       }
375     }
376   }
377 
378   LOG(LOGS_ERR, "Invalid syntax for mask/address");
379   return 0;
380 }
381 
382 /* ================================================== */
383 
384 static int
process_cmd_offline(CMD_Request * msg,char * line)385 process_cmd_offline(CMD_Request *msg, char *line)
386 {
387   IPAddr mask, address;
388   int ok;
389 
390   if (read_mask_address(line, &mask, &address)) {
391     UTI_IPHostToNetwork(&mask, &msg->data.offline.mask);
392     UTI_IPHostToNetwork(&address, &msg->data.offline.address);
393     msg->command = htons(REQ_OFFLINE);
394     ok = 1;
395   } else {
396     ok = 0;
397   }
398 
399   return ok;
400 
401 }
402 
403 /* ================================================== */
404 
405 
406 static int
process_cmd_online(CMD_Request * msg,char * line)407 process_cmd_online(CMD_Request *msg, char *line)
408 {
409   IPAddr mask, address;
410   int ok;
411 
412   if (read_mask_address(line, &mask, &address)) {
413     UTI_IPHostToNetwork(&mask, &msg->data.online.mask);
414     UTI_IPHostToNetwork(&address, &msg->data.online.address);
415     msg->command = htons(REQ_ONLINE);
416     ok = 1;
417   } else {
418     ok = 0;
419   }
420 
421   return ok;
422 
423 }
424 
425 /* ================================================== */
426 
427 static void
process_cmd_onoffline(CMD_Request * msg,char * line)428 process_cmd_onoffline(CMD_Request *msg, char *line)
429 {
430   msg->command = htons(REQ_ONOFFLINE);
431 }
432 
433 /* ================================================== */
434 
435 static int
read_address_integer(char * line,IPAddr * address,int * value)436 read_address_integer(char *line, IPAddr *address, int *value)
437 {
438   char *hostname;
439   int ok = 0;
440 
441   hostname = line;
442   line = CPS_SplitWord(line);
443 
444   if (sscanf(line, "%d", value) != 1) {
445     LOG(LOGS_ERR, "Invalid syntax for address value");
446     ok = 0;
447   } else {
448     if (!parse_source_address(hostname, address)) {
449       LOG(LOGS_ERR, "Could not get address for hostname");
450       ok = 0;
451     } else {
452       ok = 1;
453     }
454   }
455 
456   return ok;
457 
458 }
459 
460 
461 /* ================================================== */
462 
463 static int
read_address_double(char * line,IPAddr * address,double * value)464 read_address_double(char *line, IPAddr *address, double *value)
465 {
466   char *hostname;
467   int ok = 0;
468 
469   hostname = line;
470   line = CPS_SplitWord(line);
471 
472   if (sscanf(line, "%lf", value) != 1) {
473     LOG(LOGS_ERR, "Invalid syntax for address value");
474     ok = 0;
475   } else {
476     if (!parse_source_address(hostname, address)) {
477       LOG(LOGS_ERR, "Could not get address for hostname");
478       ok = 0;
479     } else {
480       ok = 1;
481     }
482   }
483 
484   return ok;
485 
486 }
487 
488 
489 /* ================================================== */
490 
491 static int
process_cmd_minpoll(CMD_Request * msg,char * line)492 process_cmd_minpoll(CMD_Request *msg, char *line)
493 {
494   IPAddr address;
495   int minpoll;
496   int ok;
497 
498   if (read_address_integer(line, &address, &minpoll)) {
499     UTI_IPHostToNetwork(&address, &msg->data.modify_minpoll.address);
500     msg->data.modify_minpoll.new_minpoll = htonl(minpoll);
501     msg->command = htons(REQ_MODIFY_MINPOLL);
502     ok = 1;
503   } else {
504     ok = 0;
505   }
506 
507   return ok;
508 
509 }
510 
511 /* ================================================== */
512 
513 static int
process_cmd_maxpoll(CMD_Request * msg,char * line)514 process_cmd_maxpoll(CMD_Request *msg, char *line)
515 {
516   IPAddr address;
517   int maxpoll;
518   int ok;
519 
520   if (read_address_integer(line, &address, &maxpoll)) {
521     UTI_IPHostToNetwork(&address, &msg->data.modify_maxpoll.address);
522     msg->data.modify_maxpoll.new_maxpoll = htonl(maxpoll);
523     msg->command = htons(REQ_MODIFY_MAXPOLL);
524     ok = 1;
525   } else {
526     ok = 0;
527   }
528 
529   return ok;
530 
531 }
532 
533 /* ================================================== */
534 
535 static int
process_cmd_maxdelay(CMD_Request * msg,char * line)536 process_cmd_maxdelay(CMD_Request *msg, char *line)
537 {
538   IPAddr address;
539   double max_delay;
540   int ok;
541 
542   if (read_address_double(line, &address, &max_delay)) {
543     UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelay.address);
544     msg->data.modify_maxdelay.new_max_delay = UTI_FloatHostToNetwork(max_delay);
545     msg->command = htons(REQ_MODIFY_MAXDELAY);
546     ok = 1;
547   } else {
548     ok = 0;
549   }
550 
551   return ok;
552 
553 }
554 
555 /* ================================================== */
556 
557 static int
process_cmd_maxdelaydevratio(CMD_Request * msg,char * line)558 process_cmd_maxdelaydevratio(CMD_Request *msg, char *line)
559 {
560   IPAddr address;
561   double max_delay_dev_ratio;
562   int ok;
563 
564   if (read_address_double(line, &address, &max_delay_dev_ratio)) {
565     UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelaydevratio.address);
566     msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_dev_ratio);
567     msg->command = htons(REQ_MODIFY_MAXDELAYDEVRATIO);
568     ok = 1;
569   } else {
570     ok = 0;
571   }
572 
573   return ok;
574 
575 }
576 
577 /* ================================================== */
578 
579 static int
process_cmd_maxdelayratio(CMD_Request * msg,char * line)580 process_cmd_maxdelayratio(CMD_Request *msg, char *line)
581 {
582   IPAddr address;
583   double max_delay_ratio;
584   int ok;
585 
586   if (read_address_double(line, &address, &max_delay_ratio)) {
587     UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelayratio.address);
588     msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_ratio);
589     msg->command = htons(REQ_MODIFY_MAXDELAYRATIO);
590     ok = 1;
591   } else {
592     ok = 0;
593   }
594 
595   return ok;
596 
597 }
598 
599 /* ================================================== */
600 
601 static int
process_cmd_minstratum(CMD_Request * msg,char * line)602 process_cmd_minstratum(CMD_Request *msg, char *line)
603 {
604   IPAddr address;
605   int min_stratum;
606   int ok;
607 
608   if (read_address_integer(line, &address, &min_stratum)) {
609     UTI_IPHostToNetwork(&address, &msg->data.modify_minstratum.address);
610     msg->data.modify_minstratum.new_min_stratum = htonl(min_stratum);
611     msg->command = htons(REQ_MODIFY_MINSTRATUM);
612     ok = 1;
613   } else {
614     ok = 0;
615   }
616 
617   return ok;
618 
619 }
620 
621 /* ================================================== */
622 
623 static int
process_cmd_polltarget(CMD_Request * msg,char * line)624 process_cmd_polltarget(CMD_Request *msg, char *line)
625 {
626   IPAddr address;
627   int poll_target;
628   int ok;
629 
630   if (read_address_integer(line, &address, &poll_target)) {
631     UTI_IPHostToNetwork(&address, &msg->data.modify_polltarget.address);
632     msg->data.modify_polltarget.new_poll_target = htonl(poll_target);
633     msg->command = htons(REQ_MODIFY_POLLTARGET);
634     ok = 1;
635   } else {
636     ok = 0;
637   }
638 
639   return ok;
640 
641 }
642 
643 /* ================================================== */
644 
645 static int
process_cmd_maxupdateskew(CMD_Request * msg,char * line)646 process_cmd_maxupdateskew(CMD_Request *msg, char *line)
647 {
648   int ok;
649   double new_max_update_skew;
650 
651   if (sscanf(line, "%lf", &new_max_update_skew) == 1) {
652     msg->data.modify_maxupdateskew.new_max_update_skew = UTI_FloatHostToNetwork(new_max_update_skew);
653     msg->command = htons(REQ_MODIFY_MAXUPDATESKEW);
654     ok = 1;
655   } else {
656     ok = 0;
657   }
658 
659   return ok;
660 
661 }
662 
663 /* ================================================== */
664 
665 static void
process_cmd_dump(CMD_Request * msg,char * line)666 process_cmd_dump(CMD_Request *msg, char *line)
667 {
668   msg->command = htons(REQ_DUMP);
669   msg->data.dump.pad = htonl(0);
670 }
671 
672 /* ================================================== */
673 
674 static void
process_cmd_writertc(CMD_Request * msg,char * line)675 process_cmd_writertc(CMD_Request *msg, char *line)
676 {
677   msg->command = htons(REQ_WRITERTC);
678 }
679 
680 /* ================================================== */
681 
682 static void
process_cmd_trimrtc(CMD_Request * msg,char * line)683 process_cmd_trimrtc(CMD_Request *msg, char *line)
684 {
685   msg->command = htons(REQ_TRIMRTC);
686 }
687 
688 /* ================================================== */
689 
690 static void
process_cmd_cyclelogs(CMD_Request * msg,char * line)691 process_cmd_cyclelogs(CMD_Request *msg, char *line)
692 {
693   msg->command = htons(REQ_CYCLELOGS);
694 }
695 
696 /* ================================================== */
697 
698 static int
process_cmd_burst(CMD_Request * msg,char * line)699 process_cmd_burst(CMD_Request *msg, char *line)
700 {
701   int n_good_samples, n_total_samples;
702   char *s1, *s2;
703   IPAddr address, mask;
704 
705   s1 = line;
706   s2 = CPS_SplitWord(s1);
707   CPS_SplitWord(s2);
708 
709   if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) {
710     LOG(LOGS_ERR, "Invalid syntax for burst command");
711     return 0;
712   }
713 
714   mask.family = address.family = IPADDR_UNSPEC;
715   if (*s2 && !read_mask_address(s2, &mask, &address)) {
716     return 0;
717   }
718 
719   msg->command = htons(REQ_BURST);
720   msg->data.burst.n_good_samples = ntohl(n_good_samples);
721   msg->data.burst.n_total_samples = ntohl(n_total_samples);
722 
723   UTI_IPHostToNetwork(&mask, &msg->data.burst.mask);
724   UTI_IPHostToNetwork(&address, &msg->data.burst.address);
725 
726   return 1;
727 }
728 
729 /* ================================================== */
730 
731 static int
process_cmd_local(CMD_Request * msg,char * line)732 process_cmd_local(CMD_Request *msg, char *line)
733 {
734   int on_off, stratum = 0, orphan = 0;
735   double distance = 0.0;
736 
737   if (!strcmp(line, "off")) {
738     on_off = 0;
739   } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) {
740     on_off = 1;
741   } else {
742     LOG(LOGS_ERR, "Invalid syntax for local command");
743     return 0;
744   }
745 
746   msg->command = htons(REQ_LOCAL2);
747   msg->data.local.on_off = htonl(on_off);
748   msg->data.local.stratum = htonl(stratum);
749   msg->data.local.distance = UTI_FloatHostToNetwork(distance);
750   msg->data.local.orphan = htonl(orphan);
751 
752   return 1;
753 }
754 
755 /* ================================================== */
756 
757 static int
process_cmd_manual(CMD_Request * msg,const char * line)758 process_cmd_manual(CMD_Request *msg, const char *line)
759 {
760   const char *p;
761 
762   p = line;
763 
764   if (!strcmp(p, "off")) {
765     msg->data.manual.option = htonl(0);
766   } else if (!strcmp(p, "on")) {
767     msg->data.manual.option = htonl(1);
768   } else if (!strcmp(p, "reset")) {
769     msg->data.manual.option = htonl(2);
770   } else {
771     LOG(LOGS_ERR, "Invalid syntax for manual command");
772     return 0;
773   }
774   msg->command = htons(REQ_MANUAL);
775 
776   return 1;
777 }
778 
779 /* ================================================== */
780 
781 static int
process_cmd_allowdeny(CMD_Request * msg,char * line,int cmd,int allcmd)782 process_cmd_allowdeny(CMD_Request *msg, char *line, int cmd, int allcmd)
783 {
784   int all, subnet_bits;
785   IPAddr ip;
786 
787   if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits)) {
788     LOG(LOGS_ERR, "Could not read address");
789     return 0;
790   }
791 
792   msg->command = htons(all ? allcmd : cmd);
793   UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
794   msg->data.allow_deny.subnet_bits = htonl(subnet_bits);
795 
796   return 1;
797 }
798 
799 /* ================================================== */
800 
801 static int
process_cmd_accheck(CMD_Request * msg,char * line)802 process_cmd_accheck(CMD_Request *msg, char *line)
803 {
804   IPAddr ip;
805   msg->command = htons(REQ_ACCHECK);
806   if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) {
807     UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
808     return 1;
809   } else {
810     LOG(LOGS_ERR, "Could not read address");
811     return 0;
812   }
813 }
814 
815 /* ================================================== */
816 
817 static int
process_cmd_cmdaccheck(CMD_Request * msg,char * line)818 process_cmd_cmdaccheck(CMD_Request *msg, char *line)
819 {
820   IPAddr ip;
821   msg->command = htons(REQ_CMDACCHECK);
822   if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) {
823     UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
824     return 1;
825   } else {
826     LOG(LOGS_ERR, "Could not read address");
827     return 0;
828   }
829 }
830 
831 /* ================================================== */
832 
833 static int
process_cmd_dfreq(CMD_Request * msg,char * line)834 process_cmd_dfreq(CMD_Request *msg, char *line)
835 {
836   double dfreq;
837 
838   msg->command = htons(REQ_DFREQ);
839 
840   if (sscanf(line, "%lf", &dfreq) != 1) {
841     LOG(LOGS_ERR, "Invalid value");
842     return 0;
843   }
844 
845   msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq);
846   return 1;
847 }
848 
849 /* ================================================== */
850 
851 static int
process_cmd_doffset(CMD_Request * msg,char * line)852 process_cmd_doffset(CMD_Request *msg, char *line)
853 {
854   double doffset;
855 
856   msg->command = htons(REQ_DOFFSET2);
857 
858   if (sscanf(line, "%lf", &doffset) != 1) {
859     LOG(LOGS_ERR, "Invalid value");
860     return 0;
861   }
862 
863   msg->data.doffset.doffset = UTI_FloatHostToNetwork(doffset);
864   return 1;
865 }
866 
867 /* ================================================== */
868 
869 static int
process_cmd_add_source(CMD_Request * msg,char * line)870 process_cmd_add_source(CMD_Request *msg, char *line)
871 {
872   CPS_NTP_Source data;
873   IPAddr ip_addr;
874   int result = 0, status, type;
875   const char *opt_name, *word;
876 
877   msg->command = htons(REQ_ADD_SOURCE);
878 
879   word = line;
880   line = CPS_SplitWord(line);
881 
882   if (!strcasecmp(word, "server")) {
883     type = REQ_ADDSRC_SERVER;
884   } else if (!strcasecmp(word, "peer")) {
885     type = REQ_ADDSRC_PEER;
886   } else if (!strcasecmp(word, "pool")) {
887     type = REQ_ADDSRC_POOL;
888   } else {
889     LOG(LOGS_ERR, "Invalid syntax for add command");
890     return 0;
891   }
892 
893   status = CPS_ParseNTPSourceAdd(line, &data);
894   switch (status) {
895     case 0:
896       LOG(LOGS_ERR, "Invalid syntax for add command");
897       break;
898     default:
899       /* Verify that the address is resolvable (chronyc and chronyd are
900          assumed to be running on the same host) */
901       if (strlen(data.name) >= sizeof (msg->data.ntp_source.name) ||
902           DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
903         LOG(LOGS_ERR, "Invalid host/IP address");
904         break;
905       }
906 
907       opt_name = NULL;
908       if (opt_name) {
909         LOG(LOGS_ERR, "%s can't be set in chronyc", opt_name);
910         break;
911       }
912 
913       msg->data.ntp_source.type = htonl(type);
914       if (strlen(data.name) >= sizeof (msg->data.ntp_source.name))
915         assert(0);
916       strncpy((char *)msg->data.ntp_source.name, data.name,
917               sizeof (msg->data.ntp_source.name));
918       msg->data.ntp_source.port = htonl(data.port);
919       msg->data.ntp_source.minpoll = htonl(data.params.minpoll);
920       msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll);
921       msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll);
922       msg->data.ntp_source.min_stratum = htonl(data.params.min_stratum);
923       msg->data.ntp_source.poll_target = htonl(data.params.poll_target);
924       msg->data.ntp_source.version = htonl(data.params.version);
925       msg->data.ntp_source.max_sources = htonl(data.params.max_sources);
926       msg->data.ntp_source.min_samples = htonl(data.params.min_samples);
927       msg->data.ntp_source.max_samples = htonl(data.params.max_samples);
928       msg->data.ntp_source.authkey = htonl(data.params.authkey);
929       msg->data.ntp_source.nts_port = htonl(data.params.nts_port);
930       msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay);
931       msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio);
932       msg->data.ntp_source.max_delay_dev_ratio =
933         UTI_FloatHostToNetwork(data.params.max_delay_dev_ratio);
934       msg->data.ntp_source.min_delay = UTI_FloatHostToNetwork(data.params.min_delay);
935       msg->data.ntp_source.asymmetry = UTI_FloatHostToNetwork(data.params.asymmetry);
936       msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset);
937       msg->data.ntp_source.flags = htonl(
938           (data.params.connectivity == SRC_ONLINE ? REQ_ADDSRC_ONLINE : 0) |
939           (data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
940           (data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
941           (data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
942           (data.params.burst ? REQ_ADDSRC_BURST : 0) |
943           (data.params.nts ? REQ_ADDSRC_NTS : 0) |
944           (data.params.copy ? REQ_ADDSRC_COPY : 0) |
945           (data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 0) |
946           (data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
947           (data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
948           (data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
949           (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
950       msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
951       msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
952       memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
953 
954       result = 1;
955 
956       break;
957   }
958 
959   return result;
960 }
961 
962 /* ================================================== */
963 
964 static int
process_cmd_delete(CMD_Request * msg,char * line)965 process_cmd_delete(CMD_Request *msg, char *line)
966 {
967   char *hostname;
968   int ok = 0;
969   IPAddr address;
970 
971   msg->command = htons(REQ_DEL_SOURCE);
972   hostname = line;
973   CPS_SplitWord(line);
974 
975   if (!*hostname) {
976     LOG(LOGS_ERR, "Invalid syntax for address");
977     ok = 0;
978   } else {
979     if (!parse_source_address(hostname, &address)) {
980       LOG(LOGS_ERR, "Could not get address for hostname");
981       ok = 0;
982     } else {
983       UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr);
984       ok = 1;
985     }
986   }
987 
988   return ok;
989 
990 }
991 
992 /* ================================================== */
993 
994 static void
give_help(void)995 give_help(void)
996 {
997   int line, len;
998   const char *s, cols[] =
999     "System clock:\0\0"
1000     "tracking\0Display system time information\0"
1001     "makestep\0Correct clock by stepping immediately\0"
1002     "makestep <threshold> <updates>\0Configure automatic clock stepping\0"
1003     "maxupdateskew <skew>\0Modify maximum valid skew to update frequency\0"
1004     "waitsync [<max-tries> [<max-correction> [<max-skew> [<interval>]]]]\0"
1005                           "Wait until synchronised in specified limits\0"
1006     "\0\0"
1007     "Time sources:\0\0"
1008     "sources [-a] [-v]\0Display information about current sources\0"
1009     "sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
1010     "selectdata [-a] [-v]\0Display information about source selection\0"
1011     "reselect\0Force reselecting synchronisation source\0"
1012     "reselectdist <dist>\0Modify reselection distance\0"
1013     "\0\0"
1014     "NTP sources:\0\0"
1015     "activity\0Check how many NTP sources are online/offline\0"
1016     "authdata [-a] [-v]\0Display information about authentication\0"
1017     "ntpdata [<address>]\0Display information about last valid measurement\0"
1018     "add server <name> [options]\0Add new NTP server\0"
1019     "add pool <name> [options]\0Add new pool of NTP servers\0"
1020     "add peer <name> [options]\0Add new NTP peer\0"
1021     "delete <address>\0Remove server or peer\0"
1022     "burst <n-good>/<n-max> [[<mask>/]<address>]\0Start rapid set of measurements\0"
1023     "maxdelay <address> <delay>\0Modify maximum valid sample delay\0"
1024     "maxdelayratio <address> <ratio>\0Modify maximum valid delay/minimum ratio\0"
1025     "maxdelaydevratio <address> <ratio>\0Modify maximum valid delay/deviation ratio\0"
1026     "minpoll <address> <poll>\0Modify minimum polling interval\0"
1027     "maxpoll <address> <poll>\0Modify maximum polling interval\0"
1028     "minstratum <address> <stratum>\0Modify minimum stratum\0"
1029     "offline [[<mask>/]<address>]\0Set sources in subnet to offline status\0"
1030     "online [[<mask>/]<address>]\0Set sources in subnet to online status\0"
1031     "onoffline\0Set all sources to online or offline status\0"
1032     "\0according to network configuration\0"
1033     "polltarget <address> <target>\0Modify poll target\0"
1034     "refresh\0Refresh IP addresses\0"
1035     "reload sources\0Re-read *.sources files\0"
1036     "sourcename <address>\0Display original name\0"
1037     "\0\0"
1038     "Manual time input:\0\0"
1039     "manual off|on|reset\0Disable/enable/reset settime command\0"
1040     "manual list\0Show previous settime entries\0"
1041     "manual delete <index>\0Delete previous settime entry\0"
1042     "settime <time>\0Set daemon time\0"
1043     "\0(e.g. Sep 25, 2015 16:30:05 or 16:30:05)\0"
1044     "\0\0NTP access:\0\0"
1045     "accheck <address>\0Check whether address is allowed\0"
1046     "clients [-p <packets>] [-k] [-r]\0Report on clients that accessed the server\0"
1047     "serverstats\0Display statistics of the server\0"
1048     "allow [<subnet>]\0Allow access to subnet as a default\0"
1049     "allow all [<subnet>]\0Allow access to subnet and all children\0"
1050     "deny [<subnet>]\0Deny access to subnet as a default\0"
1051     "deny all [<subnet>]\0Deny access to subnet and all children\0"
1052     "local [options]\0Serve time even when not synchronised\0"
1053     "local off\0Don't serve time when not synchronised\0"
1054     "smoothtime reset|activate\0Reset/activate time smoothing\0"
1055     "smoothing\0Display current time smoothing state\0"
1056     "\0\0"
1057     "Monitoring access:\0\0"
1058     "cmdaccheck <address>\0Check whether address is allowed\0"
1059     "cmdallow [<subnet>]\0Allow access to subnet as a default\0"
1060     "cmdallow all [<subnet>]\0Allow access to subnet and all children\0"
1061     "cmddeny [<subnet>]\0Deny access to subnet as a default\0"
1062     "cmddeny all [<subnet>]\0Deny access to subnet and all children\0"
1063     "\0\0"
1064     "Real-time clock:\0\0"
1065     "rtcdata\0Print current RTC performance parameters\0"
1066     "trimrtc\0Correct RTC relative to system clock\0"
1067     "writertc\0Save RTC performance parameters to file\0"
1068     "\0\0"
1069     "Other daemon commands:\0\0"
1070     "cyclelogs\0Close and re-open log files\0"
1071     "dump\0Dump measurements and NTS keys/cookies\0"
1072     "rekey\0Re-read keys\0"
1073     "reset sources\0Drop all measurements\0"
1074     "shutdown\0Stop daemon\0"
1075     "\0\0"
1076     "Client commands:\0\0"
1077     "dns -n|+n\0Disable/enable resolving IP addresses to hostnames\0"
1078     "dns -4|-6|-46\0Resolve hostnames only to IPv4/IPv6/both addresses\0"
1079     "timeout <milliseconds>\0Set initial response timeout\0"
1080     "retries <retries>\0Set maximum number of retries\0"
1081     "keygen [<id> [<type> [<bits>]]]\0Generate key for key file\0"
1082     "exit|quit\0Leave the program\0"
1083     "help\0Generate this help\0"
1084     "\0";
1085 
1086   /* Indent the second column */
1087   for (s = cols, line = 0; s < cols + sizeof (cols); s += len + 1, line++) {
1088     len = strlen(s);
1089     printf(line % 2 == 0 ? (len >= 28 ? "%s\n%28s" : "%-28s%s") : "%s%s\n",
1090            s, "");
1091   }
1092 }
1093 
1094 /* ================================================== */
1095 /* Tab-completion when editline is available */
1096 
1097 #ifdef FEAT_READLINE
1098 
1099 enum {
1100   TAB_COMPLETE_BASE_CMDS,
1101   TAB_COMPLETE_ADD_OPTS,
1102   TAB_COMPLETE_MANUAL_OPTS,
1103   TAB_COMPLETE_RELOAD_OPTS,
1104   TAB_COMPLETE_RESET_OPTS,
1105   TAB_COMPLETE_SOURCES_OPTS,
1106   TAB_COMPLETE_SOURCESTATS_OPTS,
1107   TAB_COMPLETE_AUTHDATA_OPTS,
1108   TAB_COMPLETE_SELECTDATA_OPTS,
1109   TAB_COMPLETE_MAX_INDEX
1110 };
1111 
1112 static int tab_complete_index;
1113 
1114 static char *
command_name_generator(const char * text,int state)1115 command_name_generator(const char *text, int state)
1116 {
1117   const char *name, **names[TAB_COMPLETE_MAX_INDEX];
1118   const char *base_commands[] = {
1119     "accheck", "activity", "add", "allow", "authdata", "burst",
1120     "clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
1121     "deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
1122     "manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
1123     "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
1124     "polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
1125     "retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing",
1126     "smoothtime", "sourcename", "sources", "sourcestats",
1127     "timeout", "tracking", "trimrtc", "waitsync", "writertc",
1128     NULL
1129   };
1130   const char *add_options[] = { "peer", "pool", "server", NULL };
1131   const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL };
1132   const char *reset_options[] = { "sources", NULL };
1133   const char *reload_options[] = { "sources", NULL };
1134   const char *common_source_options[] = { "-a", "-v", NULL };
1135   static int list_index, len;
1136 
1137   names[TAB_COMPLETE_BASE_CMDS] = base_commands;
1138   names[TAB_COMPLETE_ADD_OPTS] = add_options;
1139   names[TAB_COMPLETE_MANUAL_OPTS] = manual_options;
1140   names[TAB_COMPLETE_RELOAD_OPTS] = reload_options;
1141   names[TAB_COMPLETE_RESET_OPTS] = reset_options;
1142   names[TAB_COMPLETE_AUTHDATA_OPTS] = common_source_options;
1143   names[TAB_COMPLETE_SELECTDATA_OPTS] = common_source_options;
1144   names[TAB_COMPLETE_SOURCES_OPTS] = common_source_options;
1145   names[TAB_COMPLETE_SOURCESTATS_OPTS] = common_source_options;
1146 
1147   if (!state) {
1148     list_index = 0;
1149     len = strlen(text);
1150   }
1151 
1152   while ((name = names[tab_complete_index][list_index++])) {
1153     if (strncmp(name, text, len) == 0) {
1154       return strdup(name);
1155     }
1156   }
1157 
1158   return NULL;
1159 }
1160 
1161 /* ================================================== */
1162 
1163 static char **
command_name_completion(const char * text,int start,int end)1164 command_name_completion(const char *text, int start, int end)
1165 {
1166   char first[32];
1167 
1168   snprintf(first, MIN(sizeof (first), start + 1), "%s", rl_line_buffer);
1169   rl_attempted_completion_over = 1;
1170 
1171   if (!strcmp(first, "add ")) {
1172     tab_complete_index = TAB_COMPLETE_ADD_OPTS;
1173   } else if (!strcmp(first, "authdata ")) {
1174     tab_complete_index = TAB_COMPLETE_AUTHDATA_OPTS;
1175   } else if (!strcmp(first, "manual ")) {
1176     tab_complete_index = TAB_COMPLETE_MANUAL_OPTS;
1177   } else if (!strcmp(first, "reload ")) {
1178     tab_complete_index = TAB_COMPLETE_RELOAD_OPTS;
1179   } else if (!strcmp(first, "reset ")) {
1180     tab_complete_index = TAB_COMPLETE_RESET_OPTS;
1181   } else if (!strcmp(first, "selectdata ")) {
1182     tab_complete_index = TAB_COMPLETE_SELECTDATA_OPTS;
1183   } else if (!strcmp(first, "sources ")) {
1184     tab_complete_index = TAB_COMPLETE_SOURCES_OPTS;
1185   } else if (!strcmp(first, "sourcestats ")) {
1186     tab_complete_index = TAB_COMPLETE_SOURCESTATS_OPTS;
1187   } else if (first[0] == '\0') {
1188     tab_complete_index = TAB_COMPLETE_BASE_CMDS;
1189   } else {
1190     return NULL;
1191   }
1192 
1193   return rl_completion_matches(text, command_name_generator);
1194 }
1195 #endif
1196 
1197 /* ================================================== */
1198 
1199 static int max_retries = 2;
1200 static int initial_timeout = 1000;
1201 static int proto_version = PROTO_VERSION_NUMBER;
1202 
1203 /* This is the core protocol module.  Complete particular fields in
1204    the outgoing packet, send it, wait for a response, handle retries,
1205    etc.  Returns a Boolean indicating whether the protocol was
1206    successful or not.*/
1207 
1208 static int
submit_request(CMD_Request * request,CMD_Reply * reply)1209 submit_request(CMD_Request *request, CMD_Reply *reply)
1210 {
1211   int select_status;
1212   int recv_status;
1213   int read_length;
1214   int command_length;
1215   int padding_length;
1216   struct timespec ts_now, ts_start;
1217   struct timeval tv;
1218   int n_attempts, new_attempt;
1219   double timeout;
1220   fd_set rdfd;
1221 
1222   request->pkt_type = PKT_TYPE_CMD_REQUEST;
1223   request->res1 = 0;
1224   request->res2 = 0;
1225   request->pad1 = 0;
1226   request->pad2 = 0;
1227 
1228   n_attempts = 0;
1229   new_attempt = 1;
1230 
1231   do {
1232     if (gettimeofday(&tv, NULL))
1233       return 0;
1234 
1235     if (new_attempt) {
1236       new_attempt = 0;
1237 
1238       if (n_attempts > max_retries)
1239         return 0;
1240 
1241       UTI_TimevalToTimespec(&tv, &ts_start);
1242 
1243       UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence));
1244       request->attempt = htons(n_attempts);
1245       request->version = proto_version;
1246       command_length = PKL_CommandLength(request);
1247       padding_length = PKL_CommandPaddingLength(request);
1248       assert(command_length > 0 && command_length > padding_length);
1249 
1250       n_attempts++;
1251 
1252       /* Zero the padding to not send any uninitialized data */
1253       memset(((char *)request) + command_length - padding_length, 0, padding_length);
1254 
1255       if (sock_fd < 0) {
1256         DEBUG_LOG("No socket to send request");
1257         return 0;
1258       }
1259 
1260       if (SCK_Send(sock_fd, (void *)request, command_length, 0) < 0)
1261         return 0;
1262     }
1263 
1264     UTI_TimevalToTimespec(&tv, &ts_now);
1265 
1266     /* Check if the clock wasn't stepped back */
1267     if (UTI_CompareTimespecs(&ts_now, &ts_start) < 0)
1268       ts_start = ts_now;
1269 
1270     timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
1271               UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
1272     DEBUG_LOG("Timeout %f seconds", timeout);
1273 
1274     /* Avoid calling select() with an invalid timeout */
1275     if (timeout <= 0.0) {
1276       new_attempt = 1;
1277       continue;
1278     }
1279 
1280     UTI_DoubleToTimeval(timeout, &tv);
1281 
1282     FD_ZERO(&rdfd);
1283     FD_SET(sock_fd, &rdfd);
1284 
1285     if (quit)
1286       return 0;
1287 
1288     select_status = select(sock_fd + 1, &rdfd, NULL, NULL, &tv);
1289 
1290     if (select_status < 0) {
1291       DEBUG_LOG("select failed : %s", strerror(errno));
1292       return 0;
1293     } else if (select_status == 0) {
1294       /* Timeout must have elapsed, try a resend? */
1295       new_attempt = 1;
1296     } else {
1297       recv_status = SCK_Receive(sock_fd, reply, sizeof (*reply), 0);
1298 
1299       if (recv_status < 0) {
1300         new_attempt = 1;
1301       } else {
1302         read_length = recv_status;
1303 
1304         /* Check if the header is valid */
1305         if (read_length < offsetof(CMD_Reply, data) ||
1306             (reply->version != proto_version &&
1307              !(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
1308                ntohs(reply->status) == STT_BADPKTVERSION)) ||
1309             reply->pkt_type != PKT_TYPE_CMD_REPLY ||
1310             reply->res1 != 0 ||
1311             reply->res2 != 0 ||
1312             reply->command != request->command ||
1313             reply->sequence != request->sequence) {
1314           DEBUG_LOG("Invalid reply");
1315           continue;
1316         }
1317 
1318 #if PROTO_VERSION_NUMBER == 6
1319         /* Protocol version 5 is similar to 6 except there is no padding.
1320            If a version 5 reply with STT_BADPKTVERSION is received,
1321            switch our version and try again. */
1322         if (proto_version == PROTO_VERSION_NUMBER &&
1323             reply->version == PROTO_VERSION_NUMBER - 1) {
1324           proto_version = PROTO_VERSION_NUMBER - 1;
1325           n_attempts--;
1326           new_attempt = 1;
1327           continue;
1328         }
1329 #else
1330 #error unknown compatibility with PROTO_VERSION - 1
1331 #endif
1332 
1333         /* Check that the packet contains all data it is supposed to have.
1334            Unknown responses will always pass this test as their expected
1335            length is zero. */
1336         if (read_length < PKL_ReplyLength(reply)) {
1337           DEBUG_LOG("Reply too short");
1338           new_attempt = 1;
1339           continue;
1340         }
1341 
1342         /* Good packet received, print out results */
1343         DEBUG_LOG("Reply cmd=%d reply=%d stat=%d",
1344                   ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
1345         break;
1346       }
1347     }
1348   } while (1);
1349 
1350   return 1;
1351 }
1352 
1353 /* ================================================== */
1354 
1355 static int
request_reply(CMD_Request * request,CMD_Reply * reply,int requested_reply,int verbose)1356 request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int verbose)
1357 {
1358   int status;
1359 
1360   while (!submit_request(request, reply)) {
1361     /* Try connecting to other addresses before giving up */
1362     if (open_io())
1363       continue;
1364     printf("506 Cannot talk to daemon\n");
1365     return 0;
1366   }
1367 
1368   status = ntohs(reply->status);
1369 
1370   if (verbose || status != STT_SUCCESS) {
1371     switch (status) {
1372       case STT_SUCCESS:
1373         printf("200 OK");
1374         break;
1375       case STT_ACCESSALLOWED:
1376         printf("208 Access allowed");
1377         break;
1378       case STT_ACCESSDENIED:
1379         printf("209 Access denied");
1380         break;
1381       case STT_FAILED:
1382         printf("500 Failure");
1383         break;
1384       case STT_UNAUTH:
1385         printf("501 Not authorised");
1386         break;
1387       case STT_INVALID:
1388         printf("502 Invalid command");
1389         break;
1390       case STT_NOSUCHSOURCE:
1391         printf("503 No such source");
1392         break;
1393       case STT_INVALIDTS:
1394         printf("504 Duplicate or stale logon detected");
1395         break;
1396       case STT_NOTENABLED:
1397         printf("505 Facility not enabled in daemon");
1398         break;
1399       case STT_BADSUBNET:
1400         printf("507 Bad subnet");
1401         break;
1402       case STT_NOHOSTACCESS:
1403         printf("510 No command access from this host");
1404         break;
1405       case STT_SOURCEALREADYKNOWN:
1406         printf("511 Source already present");
1407         break;
1408       case STT_TOOMANYSOURCES:
1409         printf("512 Too many sources present");
1410         break;
1411       case STT_NORTC:
1412         printf("513 RTC driver not running");
1413         break;
1414       case STT_BADRTCFILE:
1415         printf("514 Can't write RTC parameters");
1416         break;
1417       case STT_INVALIDAF:
1418         printf("515 Invalid address family");
1419         break;
1420       case STT_BADSAMPLE:
1421         printf("516 Sample index out of range");
1422         break;
1423       case STT_BADPKTVERSION:
1424         printf("517 Protocol version mismatch");
1425         break;
1426       case STT_BADPKTLENGTH:
1427         printf("518 Packet length mismatch");
1428         break;
1429       case STT_INACTIVE:
1430         printf("519 Client logging is not active in the daemon");
1431         break;
1432       case STT_INVALIDNAME:
1433         printf("521 Invalid name");
1434         break;
1435       default:
1436         printf("520 Got unexpected error from daemon");
1437     }
1438     printf("\n");
1439   }
1440 
1441   if (status != STT_SUCCESS &&
1442       status != STT_ACCESSALLOWED && status != STT_ACCESSDENIED) {
1443     return 0;
1444   }
1445 
1446   if (ntohs(reply->reply) != requested_reply) {
1447     printf("508 Bad reply from daemon\n");
1448     return 0;
1449   }
1450 
1451   /* Make sure an unknown response was not requested */
1452   assert(PKL_ReplyLength(reply));
1453 
1454   return 1;
1455 }
1456 
1457 /* ================================================== */
1458 
1459 static void
print_seconds(unsigned long s)1460 print_seconds(unsigned long s)
1461 {
1462   unsigned long d;
1463 
1464   if (s == (uint32_t)-1) {
1465     printf("   -");
1466   } else if (s < 1200) {
1467     printf("%4lu", s);
1468   } else if (s < 36000) {
1469     printf("%3lum", s / 60);
1470   } else if (s < 345600) {
1471     printf("%3luh", s / 3600);
1472   } else {
1473     d = s / 86400;
1474     if (d > 999) {
1475       printf("%3luy", d / 365);
1476     } else {
1477       printf("%3lud", d);
1478     }
1479   }
1480 }
1481 
1482 /* ================================================== */
1483 
1484 static void
print_nanoseconds(double s)1485 print_nanoseconds(double s)
1486 {
1487   s = fabs(s);
1488 
1489   if (s < 9999.5e-9) {
1490     printf("%4.0fns", s * 1e9);
1491   } else if (s < 9999.5e-6) {
1492     printf("%4.0fus", s * 1e6);
1493   } else if (s < 9999.5e-3) {
1494     printf("%4.0fms", s * 1e3);
1495   } else if (s < 999.5) {
1496     printf("%5.1fs", s);
1497   } else if (s < 99999.5) {
1498     printf("%5.0fs", s);
1499   } else if (s < 99999.5 * 60) {
1500     printf("%5.0fm", s / 60);
1501   } else if (s < 99999.5 * 3600) {
1502     printf("%5.0fh", s / 3600);
1503   } else if (s < 99999.5 * 3600 * 24) {
1504     printf("%5.0fd", s / (3600 * 24));
1505   } else {
1506     printf("%5.0fy", s / (3600 * 24 * 365));
1507   }
1508 }
1509 
1510 /* ================================================== */
1511 
1512 static void
print_signed_nanoseconds(double s)1513 print_signed_nanoseconds(double s)
1514 {
1515   double x;
1516 
1517   x = fabs(s);
1518 
1519   if (x < 9999.5e-9) {
1520     printf("%+5.0fns", s * 1e9);
1521   } else if (x < 9999.5e-6) {
1522     printf("%+5.0fus", s * 1e6);
1523   } else if (x < 9999.5e-3) {
1524     printf("%+5.0fms", s * 1e3);
1525   } else if (x < 999.5) {
1526     printf("%+6.1fs", s);
1527   } else if (x < 99999.5) {
1528     printf("%+6.0fs", s);
1529   } else if (x < 99999.5 * 60) {
1530     printf("%+6.0fm", s / 60);
1531   } else if (x < 99999.5 * 3600) {
1532     printf("%+6.0fh", s / 3600);
1533   } else if (x < 99999.5 * 3600 * 24) {
1534     printf("%+6.0fd", s / (3600 * 24));
1535   } else {
1536     printf("%+6.0fy", s / (3600 * 24 * 365));
1537   }
1538 }
1539 
1540 /* ================================================== */
1541 
1542 static void
print_freq_ppm(double f)1543 print_freq_ppm(double f)
1544 {
1545   if (fabs(f) < 99999.5) {
1546     printf("%10.3f", f);
1547   } else {
1548     printf("%10.0f", f);
1549   }
1550 }
1551 
1552 /* ================================================== */
1553 
1554 static void
print_signed_freq_ppm(double f)1555 print_signed_freq_ppm(double f)
1556 {
1557   if (fabs(f) < 99999.5) {
1558     printf("%+10.3f", f);
1559   } else {
1560     printf("%+10.0f", f);
1561   }
1562 }
1563 
1564 /* ================================================== */
1565 
1566 static void
print_clientlog_interval(int rate)1567 print_clientlog_interval(int rate)
1568 {
1569   if (rate >= 127) {
1570     printf(" -");
1571   } else {
1572     printf("%2d", rate);
1573   }
1574 }
1575 
1576 /* ================================================== */
1577 
1578 static void
print_header(const char * header)1579 print_header(const char *header)
1580 {
1581   int len;
1582 
1583   if (csv_mode)
1584     return;
1585 
1586   printf("%s\n", header);
1587 
1588   len = strlen(header);
1589   while (len--)
1590     printf("=");
1591   printf("\n");
1592 }
1593 
1594 /* ================================================== */
1595 
1596 #define REPORT_END 0x1234
1597 
1598 /* Print a report. The syntax of the format is similar to printf(), but not all
1599    specifiers are supported and some are different! */
1600 
1601 static void
print_report(const char * format,...)1602 print_report(const char *format, ...)
1603 {
1604   char buf[256];
1605   va_list ap;
1606   int i, field, sign, width, prec, spec;
1607   const char *string;
1608   unsigned long long_uinteger;
1609   unsigned int uinteger;
1610   int integer;
1611   struct timespec *ts;
1612   struct tm *tm;
1613   double dbl;
1614 
1615   va_start(ap, format);
1616 
1617   for (field = 0; ; field++) {
1618     /* Search for text between format specifiers and print it
1619        if not in the CSV mode */
1620     for (i = 0; i < sizeof (buf) && format[i] != '%' && format[i] != '\0'; i++)
1621       buf[i] = format[i];
1622 
1623     if (i >= sizeof (buf))
1624       break;
1625 
1626     buf[i] = '\0';
1627 
1628     if (!csv_mode)
1629       printf("%s", buf);
1630 
1631     if (format[i] == '\0' || format[i + 1] == '\0')
1632       break;
1633 
1634     format += i + 1;
1635 
1636     sign = 0;
1637     width = 0;
1638     prec = 5;
1639 
1640     if (*format == '+' || *format == '-') {
1641       sign = 1;
1642       format++;
1643     }
1644 
1645     if (isdigit((unsigned char)*format)) {
1646       width = atoi(format);
1647       while (isdigit((unsigned char)*format))
1648         format++;
1649     }
1650 
1651     if (*format == '.') {
1652       format++;
1653       prec = atoi(format);
1654       while (isdigit((unsigned char)*format))
1655         format++;
1656     }
1657 
1658     spec = *format;
1659     format++;
1660 
1661     /* Disable human-readable formatting in the CSV mode */
1662     if (csv_mode) {
1663       sign = width = 0;
1664 
1665       if (field > 0)
1666         printf(",");
1667 
1668       switch (spec) {
1669         case 'C':
1670           spec = 'd';
1671           break;
1672         case 'F':
1673         case 'P':
1674           prec = 3;
1675           spec = 'f';
1676           break;
1677         case 'O':
1678         case 'S':
1679           prec = 9;
1680           spec = 'f';
1681           break;
1682         case 'I':
1683           spec = 'U';
1684           break;
1685         case 'T':
1686           spec = 'V';
1687           break;
1688       }
1689     }
1690 
1691     switch (spec) {
1692       case 'B': /* boolean */
1693         integer = va_arg(ap, int);
1694         printf("%s", integer ? "Yes" : "No");
1695         break;
1696       case 'C': /* clientlog interval */
1697         integer = va_arg(ap, int);
1698         print_clientlog_interval(integer);
1699         break;
1700       case 'F': /* absolute frequency in ppm with fast/slow keyword */
1701       case 'O': /* absolute offset in seconds with fast/slow keyword */
1702         dbl = va_arg(ap, double);
1703         printf("%*.*f %s %s", width, prec, fabs(dbl),
1704                spec == 'O' ? "seconds" : "ppm",
1705                (dbl > 0.0) ^ (spec != 'O') ? "slow" : "fast");
1706         break;
1707       case 'I': /* interval with unit */
1708         long_uinteger = va_arg(ap, unsigned long);
1709         print_seconds(long_uinteger);
1710         break;
1711       case 'L': /* leap status */
1712         integer = va_arg(ap, int);
1713         switch (integer) {
1714           case LEAP_Normal:
1715             string = width != 1 ? "Normal" : "N";
1716             break;
1717           case LEAP_InsertSecond:
1718             string = width != 1 ? "Insert second" : "+";
1719             break;
1720           case LEAP_DeleteSecond:
1721             string = width != 1 ? "Delete second" : "-";
1722             break;
1723           case LEAP_Unsynchronised:
1724             string = width != 1 ? "Not synchronised" : "?";
1725             break;
1726           default:
1727             string = width != 1 ? "Invalid" : "?";
1728             break;
1729         }
1730         printf("%s", string);
1731         break;
1732       case 'M': /* NTP mode */
1733         integer = va_arg(ap, int);
1734         switch (integer) {
1735           case MODE_ACTIVE:
1736             string = "Symmetric active";
1737             break;
1738           case MODE_PASSIVE:
1739             string = "Symmetric passive";
1740             break;
1741           case MODE_SERVER:
1742             string = "Server";
1743             break;
1744           default:
1745             string = "Invalid";
1746             break;
1747         }
1748         printf("%s", string);
1749         break;
1750       case 'N': /* Timestamp source */
1751         integer = va_arg(ap, int);
1752         switch (integer) {
1753           case 'D':
1754             string = "Daemon";
1755             break;
1756           case 'K':
1757             string = "Kernel";
1758             break;
1759           case 'H':
1760             string = "Hardware";
1761             break;
1762           default:
1763             string = "Invalid";
1764             break;
1765         }
1766         printf("%s", string);
1767         break;
1768       case 'P': /* frequency in ppm */
1769         dbl = va_arg(ap, double);
1770         if (sign)
1771           print_signed_freq_ppm(dbl);
1772         else
1773           print_freq_ppm(dbl);
1774         break;
1775       case 'R': /* reference ID in hexdecimal */
1776         long_uinteger = va_arg(ap, unsigned long);
1777         printf("%08lX", long_uinteger);
1778         break;
1779       case 'S': /* offset with unit */
1780         dbl = va_arg(ap, double);
1781         if (sign)
1782           print_signed_nanoseconds(dbl);
1783         else
1784           print_nanoseconds(dbl);
1785         break;
1786       case 'T': /* timespec as date and time in UTC */
1787         ts = va_arg(ap, struct timespec *);
1788         tm = gmtime(&ts->tv_sec);
1789         if (!tm)
1790           break;
1791         strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm);
1792         printf("%s", buf);
1793         break;
1794       case 'U': /* unsigned long in decimal */
1795         long_uinteger = va_arg(ap, unsigned long);
1796         printf("%*lu", width, long_uinteger);
1797         break;
1798       case 'V': /* timespec as seconds since epoch */
1799         ts = va_arg(ap, struct timespec *);
1800         printf("%s", UTI_TimespecToString(ts));
1801         break;
1802       case 'b': /* unsigned int in binary */
1803         uinteger = va_arg(ap, unsigned int);
1804         for (i = prec - 1; i >= 0; i--)
1805           printf("%c", uinteger & 1U << i ? '1' : '0');
1806         break;
1807 
1808       /* Classic printf specifiers */
1809       case 'c': /* character */
1810         integer = va_arg(ap, int);
1811         printf("%c", integer);
1812         break;
1813       case 'd': /* signed int in decimal */
1814         integer = va_arg(ap, int);
1815         printf("%*d", width, integer);
1816         break;
1817       case 'f': /* double */
1818         dbl = va_arg(ap, double);
1819         printf(sign ? "%+*.*f" : "%*.*f", width, prec, dbl);
1820         break;
1821       case 'o': /* unsigned int in octal */
1822         uinteger = va_arg(ap, unsigned int);
1823         printf("%*o", width, uinteger);
1824         break;
1825       case 's': /* string */
1826         string = va_arg(ap, const char *);
1827         if (sign)
1828           printf("%-*s", width, string);
1829         else
1830           printf("%*s", width, string);
1831         break;
1832       case 'u': /* unsigned int in decimal */
1833         uinteger = va_arg(ap, unsigned int);
1834         printf("%*u", width, uinteger);
1835         break;
1836     }
1837   }
1838 
1839   /* Require terminating argument to catch bad type conversions */
1840   if (va_arg(ap, int) != REPORT_END)
1841     assert(0);
1842 
1843   va_end(ap);
1844 
1845   if (csv_mode)
1846     printf("\n");
1847 }
1848 
1849 /* ================================================== */
1850 
1851 static void
print_info_field(const char * format,...)1852 print_info_field(const char *format, ...)
1853 {
1854   va_list ap;
1855 
1856   if (csv_mode)
1857     return;
1858 
1859   va_start(ap, format);
1860   vprintf(format, ap);
1861   va_end(ap);
1862 }
1863 
1864 /* ================================================== */
1865 
1866 static int
get_source_name(IPAddr * ip_addr,char * buf,int size)1867 get_source_name(IPAddr *ip_addr, char *buf, int size)
1868 {
1869   CMD_Request request;
1870   CMD_Reply reply;
1871   int i;
1872 
1873   request.command = htons(REQ_NTP_SOURCE_NAME);
1874   UTI_IPHostToNetwork(ip_addr, &request.data.ntp_source_name.ip_addr);
1875   if (!request_reply(&request, &reply, RPY_NTP_SOURCE_NAME, 0) ||
1876       reply.data.ntp_source_name.name[sizeof (reply.data.ntp_source_name.name) - 1] != '\0' ||
1877       snprintf(buf, size, "%s", (char *)reply.data.ntp_source_name.name) >= size)
1878     return 0;
1879 
1880   /* Make sure the name is printable */
1881   for (i = 0; i < size && buf[i] != '\0'; i++) {
1882     if (!isgraph((unsigned char)buf[i]))
1883       return 0;
1884   }
1885 
1886   return 1;
1887 }
1888 
1889 /* ================================================== */
1890 
1891 static void
format_name(char * buf,int size,int trunc_dns,int ref,uint32_t ref_id,int source,IPAddr * ip_addr)1892 format_name(char *buf, int size, int trunc_dns, int ref, uint32_t ref_id,
1893             int source, IPAddr *ip_addr)
1894 {
1895   if (ref) {
1896     snprintf(buf, size, "%s", UTI_RefidToString(ref_id));
1897   } else if (source && source_names) {
1898     if (!get_source_name(ip_addr, buf, size))
1899       snprintf(buf, size, "?");
1900   } else if (no_dns || csv_mode) {
1901     snprintf(buf, size, "%s", UTI_IPToString(ip_addr));
1902   } else {
1903     DNS_IPAddress2Name(ip_addr, buf, size);
1904     if (trunc_dns > 0 && strlen(buf) > trunc_dns) {
1905       buf[trunc_dns - 1] = '>';
1906       buf[trunc_dns] = '\0';
1907     }
1908   }
1909 }
1910 
1911 /* ================================================== */
1912 
1913 static void
parse_sources_options(char * line,int * all,int * verbose)1914 parse_sources_options(char *line, int *all, int *verbose)
1915 {
1916   char *opt;
1917 
1918   *all = *verbose = 0;
1919 
1920   while (*line) {
1921     opt = line;
1922     line = CPS_SplitWord(line);
1923     if (!strcmp(opt, "-a"))
1924       *all = 1;
1925     else if (!strcmp(opt, "-v"))
1926       *verbose = !csv_mode;
1927   }
1928 }
1929 
1930 /* ================================================== */
1931 
1932 static int
process_cmd_sourcename(char * line)1933 process_cmd_sourcename(char *line)
1934 {
1935   IPAddr ip_addr;
1936   char name[256];
1937 
1938   if (!parse_source_address(line, &ip_addr)) {
1939     LOG(LOGS_ERR, "Could not read address");
1940     return 0;
1941   }
1942 
1943   if (!get_source_name(&ip_addr, name, sizeof (name)))
1944     return 0;
1945 
1946   print_report("%s\n", name, REPORT_END);
1947 
1948   return 1;
1949 }
1950 
1951 /* ================================================== */
1952 
1953 static int
process_cmd_sources(char * line)1954 process_cmd_sources(char *line)
1955 {
1956   CMD_Request request;
1957   CMD_Reply reply;
1958   IPAddr ip_addr;
1959   uint32_t i, mode, n_sources;
1960   char name[256], mode_ch, state_ch;
1961   int all, verbose;
1962 
1963   parse_sources_options(line, &all, &verbose);
1964 
1965   request.command = htons(REQ_N_SOURCES);
1966   if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
1967     return 0;
1968 
1969   n_sources = ntohl(reply.data.n_sources.n_sources);
1970 
1971   if (verbose) {
1972     printf("\n");
1973     printf("  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.\n");
1974     printf(" / .- Source state '*' = current best, '+' = combined, '-' = not combined,\n");
1975     printf("| /             'x' = may be in error, '~' = too variable, '?' = unusable.\n");
1976     printf("||                                                 .- xxxx [ yyyy ] +/- zzzz\n");
1977     printf("||      Reachability register (octal) -.           |  xxxx = adjusted offset,\n");
1978     printf("||      Log2(Polling interval) --.      |          |  yyyy = measured offset,\n");
1979     printf("||                                \\     |          |  zzzz = estimated error.\n");
1980     printf("||                                 |    |           \\\n");
1981   }
1982 
1983   print_header("MS Name/IP address         Stratum Poll Reach LastRx Last sample               ");
1984 
1985   /*           "MS NNNNNNNNNNNNNNNNNNNNNNNNNNN  SS  PP   RRR  RRRR  SSSSSSS[SSSSSSS] +/- SSSSSS" */
1986 
1987   for (i = 0; i < n_sources; i++) {
1988     request.command = htons(REQ_SOURCE_DATA);
1989     request.data.source_data.index = htonl(i);
1990     if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
1991       return 0;
1992 
1993     mode = ntohs(reply.data.source_data.mode);
1994     UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
1995     if (!all && ip_addr.family == IPADDR_ID)
1996       continue;
1997 
1998     format_name(name, sizeof (name), 25,
1999                 mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4,
2000                 ip_addr.addr.in4, 1, &ip_addr);
2001 
2002     switch (mode) {
2003       case RPY_SD_MD_CLIENT:
2004         mode_ch = '^';
2005         break;
2006       case RPY_SD_MD_PEER:
2007         mode_ch = '=';
2008         break;
2009       case RPY_SD_MD_REF:
2010         mode_ch = '#';
2011         break;
2012       default:
2013         mode_ch = ' ';
2014     }
2015 
2016     switch (ntohs(reply.data.source_data.state)) {
2017       case RPY_SD_ST_SELECTED:
2018         state_ch = '*';
2019         break;
2020       case RPY_SD_ST_NONSELECTABLE:
2021         state_ch = '?';
2022         break;
2023       case RPY_SD_ST_FALSETICKER:
2024         state_ch = 'x';
2025         break;
2026       case RPY_SD_ST_JITTERY:
2027         state_ch = '~';
2028         break;
2029       case RPY_SD_ST_UNSELECTED:
2030         state_ch = '+';
2031         break;
2032       case RPY_SD_ST_SELECTABLE:
2033         state_ch = '-';
2034         break;
2035       default:
2036         state_ch = ' ';
2037     }
2038 
2039     switch (ntohs(reply.data.source_data.flags)) {
2040       default:
2041         break;
2042     }
2043 
2044     print_report("%c%c %-27s  %2d  %2d   %3o  %I  %+S[%+S] +/- %S\n",
2045                  mode_ch, state_ch, name,
2046                  ntohs(reply.data.source_data.stratum),
2047                  (int16_t)ntohs(reply.data.source_data.poll),
2048                  ntohs(reply.data.source_data.reachability),
2049                  (unsigned long)ntohl(reply.data.source_data.since_sample),
2050                  UTI_FloatNetworkToHost(reply.data.source_data.latest_meas),
2051                  UTI_FloatNetworkToHost(reply.data.source_data.orig_latest_meas),
2052                  UTI_FloatNetworkToHost(reply.data.source_data.latest_meas_err),
2053                  REPORT_END);
2054   }
2055 
2056   return 1;
2057 }
2058 
2059 /* ================================================== */
2060 
2061 static int
process_cmd_sourcestats(char * line)2062 process_cmd_sourcestats(char *line)
2063 {
2064   CMD_Request request;
2065   CMD_Reply reply;
2066   uint32_t i, n_sources;
2067   int all, verbose;
2068   char name[256];
2069   IPAddr ip_addr;
2070 
2071   parse_sources_options(line, &all, &verbose);
2072 
2073   request.command = htons(REQ_N_SOURCES);
2074   if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
2075     return 0;
2076 
2077   n_sources = ntohl(reply.data.n_sources.n_sources);
2078 
2079   if (verbose) {
2080     printf("                             .- Number of sample points in measurement set.\n");
2081     printf("                            /    .- Number of residual runs with same sign.\n");
2082     printf("                           |    /    .- Length of measurement set (time).\n");
2083     printf("                           |   |    /      .- Est. clock freq error (ppm).\n");
2084     printf("                           |   |   |      /           .- Est. error in freq.\n");
2085     printf("                           |   |   |     |           /         .- Est. offset.\n");
2086     printf("                           |   |   |     |          |          |   On the -.\n");
2087     printf("                           |   |   |     |          |          |   samples. \\\n");
2088     printf("                           |   |   |     |          |          |             |\n");
2089   }
2090 
2091   print_header("Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev");
2092 
2093   /*           "NNNNNNNNNNNNNNNNNNNNNNNNN  NP  NR  SSSS FFFFFFFFFF SSSSSSSSSS  SSSSSSS  SSSSSS" */
2094 
2095   for (i = 0; i < n_sources; i++) {
2096     request.command = htons(REQ_SOURCESTATS);
2097     request.data.source_data.index = htonl(i);
2098     if (!request_reply(&request, &reply, RPY_SOURCESTATS, 0))
2099       return 0;
2100 
2101     UTI_IPNetworkToHost(&reply.data.sourcestats.ip_addr, &ip_addr);
2102     if (!all && ip_addr.family == IPADDR_ID)
2103       continue;
2104 
2105     format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
2106                 ntohl(reply.data.sourcestats.ref_id), 1, &ip_addr);
2107 
2108     print_report("%-25s %3U %3U  %I %+P %P  %+S  %S\n",
2109                  name,
2110                  (unsigned long)ntohl(reply.data.sourcestats.n_samples),
2111                  (unsigned long)ntohl(reply.data.sourcestats.n_runs),
2112                  (unsigned long)ntohl(reply.data.sourcestats.span_seconds),
2113                  UTI_FloatNetworkToHost(reply.data.sourcestats.resid_freq_ppm),
2114                  UTI_FloatNetworkToHost(reply.data.sourcestats.skew_ppm),
2115                  UTI_FloatNetworkToHost(reply.data.sourcestats.est_offset),
2116                  UTI_FloatNetworkToHost(reply.data.sourcestats.sd),
2117                  REPORT_END);
2118   }
2119 
2120   return 1;
2121 }
2122 
2123 /* ================================================== */
2124 
2125 static int
process_cmd_tracking(char * line)2126 process_cmd_tracking(char *line)
2127 {
2128   CMD_Request request;
2129   CMD_Reply reply;
2130   IPAddr ip_addr;
2131   uint32_t ref_id;
2132   char name[256];
2133   struct timespec ref_time;
2134 
2135   request.command = htons(REQ_TRACKING);
2136   if (!request_reply(&request, &reply, RPY_TRACKING, 0))
2137     return 0;
2138 
2139   ref_id = ntohl(reply.data.tracking.ref_id);
2140 
2141   UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr);
2142   format_name(name, sizeof (name), sizeof (name),
2143               ip_addr.family == IPADDR_UNSPEC, ref_id, 1, &ip_addr);
2144 
2145   UTI_TimespecNetworkToHost(&reply.data.tracking.ref_time, &ref_time);
2146 
2147   print_report("Reference ID    : %R (%s)\n"
2148                "Stratum         : %u\n"
2149                "Ref time (UTC)  : %T\n"
2150                "System time     : %.9O of NTP time\n"
2151                "Last offset     : %+.9f seconds\n"
2152                "RMS offset      : %.9f seconds\n"
2153                "Frequency       : %.3F\n"
2154                "Residual freq   : %+.3f ppm\n"
2155                "Skew            : %.3f ppm\n"
2156                "Root delay      : %.9f seconds\n"
2157                "Root dispersion : %.9f seconds\n"
2158                "Update interval : %.1f seconds\n"
2159                "Leap status     : %L\n",
2160                (unsigned long)ref_id, name,
2161                ntohs(reply.data.tracking.stratum),
2162                &ref_time,
2163                UTI_FloatNetworkToHost(reply.data.tracking.current_correction),
2164                UTI_FloatNetworkToHost(reply.data.tracking.last_offset),
2165                UTI_FloatNetworkToHost(reply.data.tracking.rms_offset),
2166                UTI_FloatNetworkToHost(reply.data.tracking.freq_ppm),
2167                UTI_FloatNetworkToHost(reply.data.tracking.resid_freq_ppm),
2168                UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm),
2169                UTI_FloatNetworkToHost(reply.data.tracking.root_delay),
2170                UTI_FloatNetworkToHost(reply.data.tracking.root_dispersion),
2171                UTI_FloatNetworkToHost(reply.data.tracking.last_update_interval),
2172                ntohs(reply.data.tracking.leap_status), REPORT_END);
2173 
2174   return 1;
2175 }
2176 
2177 /* ================================================== */
2178 
2179 static int
process_cmd_authdata(char * line)2180 process_cmd_authdata(char *line)
2181 {
2182   CMD_Request request;
2183   CMD_Reply reply;
2184   IPAddr ip_addr;
2185   uint32_t i, source_mode, n_sources;
2186   int all, verbose;
2187   const char *mode_str;
2188   char name[256];
2189 
2190   parse_sources_options(line, &all, &verbose);
2191 
2192   request.command = htons(REQ_N_SOURCES);
2193   if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
2194     return 0;
2195 
2196   n_sources = ntohl(reply.data.n_sources.n_sources);
2197 
2198   if (verbose) {
2199     printf(    "                             .- Auth. mechanism (NTS, SK - symmetric key)\n");
2200     printf(    "                            |   Key length -.  Cookie length (bytes) -.\n");
2201     printf(    "                            |       (bits)  |  Num. of cookies --.    |\n");
2202     printf(    "                            |               |  Key est. attempts  |   |\n");
2203     printf(    "                            |               |           |         |   |\n");
2204   }
2205 
2206   print_header("Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen");
2207 
2208   /*           "NNNNNNNNNNNNNNNNNNNNNNNNNNN MMMM KKKKK AAAA LLLL LLLL AAAA NNNN CCCC LLLL" */
2209 
2210   for (i = 0; i < n_sources; i++) {
2211     request.command = htons(REQ_SOURCE_DATA);
2212     request.data.source_data.index = htonl(i);
2213     if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
2214       return 0;
2215 
2216     source_mode = ntohs(reply.data.source_data.mode);
2217     if (source_mode != RPY_SD_MD_CLIENT && source_mode != RPY_SD_MD_PEER)
2218       continue;
2219 
2220     UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
2221     if (!all && ip_addr.family == IPADDR_ID)
2222       continue;
2223 
2224     request.command = htons(REQ_AUTH_DATA);
2225     request.data.auth_data.ip_addr = reply.data.source_data.ip_addr;
2226     if (!request_reply(&request, &reply, RPY_AUTH_DATA, 0))
2227       return 0;
2228 
2229     format_name(name, sizeof (name), 25, 0, 0, 1, &ip_addr);
2230 
2231     switch (ntohs(reply.data.auth_data.mode)) {
2232       case RPY_AD_MD_NONE:
2233         mode_str = "-";
2234         break;
2235       case RPY_AD_MD_SYMMETRIC:
2236         mode_str = "SK";
2237         break;
2238       case RPY_AD_MD_NTS:
2239         mode_str = "NTS";
2240         break;
2241       default:
2242         mode_str = "?";
2243         break;
2244     }
2245 
2246     print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
2247                  name, mode_str,
2248                  (unsigned long)ntohl(reply.data.auth_data.key_id),
2249                  ntohs(reply.data.auth_data.key_type),
2250                  ntohs(reply.data.auth_data.key_length),
2251                  (unsigned long)ntohl(reply.data.auth_data.last_ke_ago),
2252                  ntohs(reply.data.auth_data.ke_attempts),
2253                  ntohs(reply.data.auth_data.nak),
2254                  ntohs(reply.data.auth_data.cookies),
2255                  ntohs(reply.data.auth_data.cookie_length),
2256                  REPORT_END);
2257   }
2258 
2259   return 1;
2260 }
2261 
2262 /* ================================================== */
2263 
2264 static int
process_cmd_ntpdata(char * line)2265 process_cmd_ntpdata(char *line)
2266 {
2267   CMD_Request request;
2268   CMD_Reply reply;
2269   IPAddr remote_addr, local_addr;
2270   struct timespec ref_time;
2271   uint32_t i, n_sources;
2272   uint16_t mode;
2273   int specified_addr;
2274 
2275   if (*line) {
2276     specified_addr = 1;
2277     n_sources = 1;
2278   } else {
2279     specified_addr = 0;
2280     request.command = htons(REQ_N_SOURCES);
2281     if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
2282       return 0;
2283     n_sources = ntohl(reply.data.n_sources.n_sources);
2284   }
2285 
2286   for (i = 0; i < n_sources; i++) {
2287     if (specified_addr) {
2288       if (!parse_source_address(line, &remote_addr)) {
2289         LOG(LOGS_ERR, "Could not get address for hostname");
2290         return 0;
2291       }
2292     } else {
2293       request.command = htons(REQ_SOURCE_DATA);
2294       request.data.source_data.index = htonl(i);
2295       if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
2296         return 0;
2297 
2298       mode = ntohs(reply.data.source_data.mode);
2299       if (mode != RPY_SD_MD_CLIENT && mode != RPY_SD_MD_PEER)
2300         continue;
2301 
2302       UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &remote_addr);
2303       if (!UTI_IsIPReal(&remote_addr))
2304         continue;
2305     }
2306 
2307     request.command = htons(REQ_NTP_DATA);
2308     UTI_IPHostToNetwork(&remote_addr, &request.data.ntp_data.ip_addr);
2309     if (!request_reply(&request, &reply, RPY_NTP_DATA, 0))
2310       return 0;
2311 
2312     UTI_IPNetworkToHost(&reply.data.ntp_data.remote_addr, &remote_addr);
2313     UTI_IPNetworkToHost(&reply.data.ntp_data.local_addr, &local_addr);
2314     UTI_TimespecNetworkToHost(&reply.data.ntp_data.ref_time, &ref_time);
2315 
2316     if (!specified_addr && !csv_mode)
2317       printf("\n");
2318 
2319     print_report("Remote address  : %s (%R)\n"
2320                  "Remote port     : %u\n"
2321                  "Local address   : %s (%R)\n"
2322                  "Leap status     : %L\n"
2323                  "Version         : %u\n"
2324                  "Mode            : %M\n"
2325                  "Stratum         : %u\n"
2326                  "Poll interval   : %d (%.0f seconds)\n"
2327                  "Precision       : %d (%.9f seconds)\n"
2328                  "Root delay      : %.6f seconds\n"
2329                  "Root dispersion : %.6f seconds\n"
2330                  "Reference ID    : %R (%s)\n"
2331                  "Reference time  : %T\n"
2332                  "Offset          : %+.9f seconds\n"
2333                  "Peer delay      : %.9f seconds\n"
2334                  "Peer dispersion : %.9f seconds\n"
2335                  "Response time   : %.9f seconds\n"
2336                  "Jitter asymmetry: %+.2f\n"
2337                  "NTP tests       : %.3b %.3b %.4b\n"
2338                  "Interleaved     : %B\n"
2339                  "Authenticated   : %B\n"
2340                  "TX timestamping : %N\n"
2341                  "RX timestamping : %N\n"
2342                  "Total TX        : %U\n"
2343                  "Total RX        : %U\n"
2344                  "Total valid RX  : %U\n",
2345                  UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
2346                  ntohs(reply.data.ntp_data.remote_port),
2347                  UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
2348                  reply.data.ntp_data.leap, reply.data.ntp_data.version,
2349                  reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
2350                  reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
2351                  reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
2352                  UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
2353                  UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
2354                  (unsigned long)ntohl(reply.data.ntp_data.ref_id),
2355                  reply.data.ntp_data.stratum <= 1 ?
2356                    UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
2357                  &ref_time,
2358                  UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
2359                  UTI_FloatNetworkToHost(reply.data.ntp_data.peer_delay),
2360                  UTI_FloatNetworkToHost(reply.data.ntp_data.peer_dispersion),
2361                  UTI_FloatNetworkToHost(reply.data.ntp_data.response_time),
2362                  UTI_FloatNetworkToHost(reply.data.ntp_data.jitter_asymmetry),
2363                  ntohs(reply.data.ntp_data.flags) >> 7,
2364                  ntohs(reply.data.ntp_data.flags) >> 4,
2365                  ntohs(reply.data.ntp_data.flags),
2366                  ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED,
2367                  ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED,
2368                  reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char,
2369                  (unsigned long)ntohl(reply.data.ntp_data.total_tx_count),
2370                  (unsigned long)ntohl(reply.data.ntp_data.total_rx_count),
2371                  (unsigned long)ntohl(reply.data.ntp_data.total_valid_count),
2372                  REPORT_END);
2373   }
2374 
2375   return 1;
2376 }
2377 
2378 /* ================================================== */
2379 
2380 static int
process_cmd_selectdata(char * line)2381 process_cmd_selectdata(char *line)
2382 {
2383   CMD_Request request;
2384   CMD_Reply reply;
2385   uint32_t i, n_sources;
2386   int all, verbose, conf_options, eff_options;
2387   char name[256];
2388   IPAddr ip_addr;
2389 
2390   parse_sources_options(line, &all, &verbose);
2391 
2392   request.command = htons(REQ_N_SOURCES);
2393   if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
2394     return 0;
2395 
2396   n_sources = ntohl(reply.data.n_sources.n_sources);
2397 
2398   if (verbose) {
2399     printf(    "  .-- State: N - noselect, M - missing samples, d/D - large distance,\n");
2400     printf(    " /           ~ - jittery, w/W - waits for others, T - not trusted,\n");
2401     printf(    "|            x - falseticker, P - not preferred, U - waits for update,\n");
2402     printf(    "|            S - stale, O - orphan, + - combined, * - best.\n");
2403     printf(    "|        Effective options ------.  (N - noselect, P - prefer\n");
2404     printf(    "|       Configured options -.     \\  T - trust, R - require)\n");
2405     printf(    "|   Auth. enabled (Y/N) -.   \\     \\     Offset interval --.\n");
2406     printf(    "|                        |    |     |                       |\n");
2407   }
2408 
2409   print_header("S Name/IP Address        Auth COpts EOpts Last Score     Interval  Leap");
2410 
2411   /*           "S NNNNNNNNNNNNNNNNNNNNNNNNN A OOOO- OOOO- LLLL SSSSS IIIIIII IIIIIII  L" */
2412 
2413   for (i = 0; i < n_sources; i++) {
2414     request.command = htons(REQ_SELECT_DATA);
2415     request.data.source_data.index = htonl(i);
2416     if (!request_reply(&request, &reply, RPY_SELECT_DATA, 0))
2417       return 0;
2418 
2419     UTI_IPNetworkToHost(&reply.data.select_data.ip_addr, &ip_addr);
2420     if (!all && ip_addr.family == IPADDR_ID)
2421       continue;
2422 
2423     format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
2424                 ntohl(reply.data.select_data.ref_id), 1, &ip_addr);
2425 
2426     conf_options = ntohs(reply.data.select_data.conf_options);
2427     eff_options = ntohs(reply.data.select_data.eff_options);
2428 
2429     print_report("%c %-25s %c %c%c%c%c%c %c%c%c%c%c %I %5.1f %+S %+S  %1L\n",
2430                  reply.data.select_data.state_char,
2431                  name,
2432                  reply.data.select_data.authentication ? 'Y' : 'N',
2433                  conf_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-',
2434                  conf_options & RPY_SD_OPTION_PREFER ? 'P' : '-',
2435                  conf_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
2436                  conf_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
2437                  '-',
2438                  eff_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-',
2439                  eff_options & RPY_SD_OPTION_PREFER ? 'P' : '-',
2440                  eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
2441                  eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
2442                  '-',
2443                  (unsigned long)ntohl(reply.data.select_data.last_sample_ago),
2444                  UTI_FloatNetworkToHost(reply.data.select_data.score),
2445                  UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
2446                  UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
2447                  reply.data.select_data.leap,
2448                  REPORT_END);
2449   }
2450 
2451   return 1;
2452 }
2453 
2454 /* ================================================== */
2455 
2456 static int
process_cmd_serverstats(char * line)2457 process_cmd_serverstats(char *line)
2458 {
2459   CMD_Request request;
2460   CMD_Reply reply;
2461 
2462   request.command = htons(REQ_SERVER_STATS);
2463   if (!request_reply(&request, &reply, RPY_SERVER_STATS3, 0))
2464     return 0;
2465 
2466   print_report("NTP packets received       : %U\n"
2467                "NTP packets dropped        : %U\n"
2468                "Command packets received   : %U\n"
2469                "Command packets dropped    : %U\n"
2470                "Client log records dropped : %U\n"
2471                "NTS-KE connections accepted: %U\n"
2472                "NTS-KE connections dropped : %U\n"
2473                "Authenticated NTP packets  : %U\n"
2474                "Interleaved NTP packets    : %U\n"
2475                "NTP timestamps held        : %U\n"
2476                "NTP timestamp span         : %U\n",
2477                (unsigned long)ntohl(reply.data.server_stats.ntp_hits),
2478                (unsigned long)ntohl(reply.data.server_stats.ntp_drops),
2479                (unsigned long)ntohl(reply.data.server_stats.cmd_hits),
2480                (unsigned long)ntohl(reply.data.server_stats.cmd_drops),
2481                (unsigned long)ntohl(reply.data.server_stats.log_drops),
2482                (unsigned long)ntohl(reply.data.server_stats.nke_hits),
2483                (unsigned long)ntohl(reply.data.server_stats.nke_drops),
2484                (unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits),
2485                (unsigned long)ntohl(reply.data.server_stats.ntp_interleaved_hits),
2486                (unsigned long)ntohl(reply.data.server_stats.ntp_timestamps),
2487                (unsigned long)ntohl(reply.data.server_stats.ntp_span_seconds),
2488                REPORT_END);
2489 
2490   return 1;
2491 }
2492 
2493 /* ================================================== */
2494 
2495 static int
process_cmd_smoothing(char * line)2496 process_cmd_smoothing(char *line)
2497 {
2498   CMD_Request request;
2499   CMD_Reply reply;
2500   uint32_t flags;
2501 
2502   request.command = htons(REQ_SMOOTHING);
2503   if (!request_reply(&request, &reply, RPY_SMOOTHING, 0))
2504     return 0;
2505 
2506   flags = ntohl(reply.data.smoothing.flags);
2507 
2508   print_report("Active         : %B %s\n"
2509                "Offset         : %+.9f seconds\n"
2510                "Frequency      : %+.6f ppm\n"
2511                "Wander         : %+.6f ppm per second\n"
2512                "Last update    : %.1f seconds ago\n"
2513                "Remaining time : %.1f seconds\n",
2514                !!(flags & RPY_SMT_FLAG_ACTIVE),
2515                flags & RPY_SMT_FLAG_LEAPONLY ? "(leap second only)" : "",
2516                UTI_FloatNetworkToHost(reply.data.smoothing.offset),
2517                UTI_FloatNetworkToHost(reply.data.smoothing.freq_ppm),
2518                UTI_FloatNetworkToHost(reply.data.smoothing.wander_ppm),
2519                UTI_FloatNetworkToHost(reply.data.smoothing.last_update_ago),
2520                UTI_FloatNetworkToHost(reply.data.smoothing.remaining_time),
2521                REPORT_END);
2522 
2523   return 1;
2524 }
2525 
2526 /* ================================================== */
2527 
2528 static int
process_cmd_smoothtime(CMD_Request * msg,const char * line)2529 process_cmd_smoothtime(CMD_Request *msg, const char *line)
2530 {
2531   if (!strcmp(line, "reset")) {
2532     msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_RESET);
2533   } else if (!strcmp(line, "activate")) {
2534     msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_ACTIVATE);
2535   } else {
2536     LOG(LOGS_ERR, "Invalid syntax for smoothtime command");
2537     return 0;
2538   }
2539 
2540   msg->command = htons(REQ_SMOOTHTIME);
2541 
2542   return 1;
2543 }
2544 
2545 /* ================================================== */
2546 
2547 static int
process_cmd_rtcreport(char * line)2548 process_cmd_rtcreport(char *line)
2549 {
2550   CMD_Request request;
2551   CMD_Reply reply;
2552   struct timespec ref_time;
2553 
2554   request.command = htons(REQ_RTCREPORT);
2555   if (!request_reply(&request, &reply, RPY_RTC, 0))
2556     return 0;
2557 
2558   UTI_TimespecNetworkToHost(&reply.data.rtc.ref_time, &ref_time);
2559 
2560   print_report("RTC ref time (UTC) : %T\n"
2561                "Number of samples  : %u\n"
2562                "Number of runs     : %u\n"
2563                "Sample span period : %I\n"
2564                "RTC is fast by     : %12.6f seconds\n"
2565                "RTC gains time at  : %9.3f ppm\n",
2566                &ref_time,
2567                ntohs(reply.data.rtc.n_samples),
2568                ntohs(reply.data.rtc.n_runs),
2569                (unsigned long)ntohl(reply.data.rtc.span_seconds),
2570                UTI_FloatNetworkToHost(reply.data.rtc.rtc_seconds_fast),
2571                UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm),
2572                REPORT_END);
2573 
2574   return 1;
2575 }
2576 
2577 /* ================================================== */
2578 
2579 static int
process_cmd_clients(char * line)2580 process_cmd_clients(char *line)
2581 {
2582   CMD_Request request;
2583   CMD_Reply reply;
2584   IPAddr ip;
2585   uint32_t i, n_clients, next_index, n_indices, min_hits, reset;
2586   RPY_ClientAccesses_Client *client;
2587   char header[80], name[50], *opt, *arg;
2588   int nke;
2589 
2590   next_index = 0;
2591   min_hits = 0;
2592   reset = 0;
2593   nke = 0;
2594 
2595   while (*line) {
2596     opt = line;
2597     line = CPS_SplitWord(line);
2598     if (strcmp(opt, "-k") == 0) {
2599       nke = 1;
2600     } else if (strcmp(opt, "-p") == 0) {
2601       arg = line;
2602       line = CPS_SplitWord(line);
2603       if (sscanf(arg, "%"SCNu32, &min_hits) != 1) {
2604         LOG(LOGS_ERR, "Invalid syntax for clients command");
2605         return 0;
2606       }
2607     } else if (strcmp(opt, "-r") == 0) {
2608       reset = 1;
2609     }
2610   }
2611 
2612   snprintf(header, sizeof (header),
2613            "Hostname                      NTP   Drop Int IntL Last  %6s   Drop Int  Last",
2614            nke ? "NTS-KE" : "Cmd");
2615   print_header(header);
2616 
2617   while (1) {
2618     request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX3);
2619     request.data.client_accesses_by_index.first_index = htonl(next_index);
2620     request.data.client_accesses_by_index.n_clients = htonl(MAX_CLIENT_ACCESSES);
2621     request.data.client_accesses_by_index.min_hits = htonl(min_hits);
2622     request.data.client_accesses_by_index.reset = htonl(reset);
2623 
2624     if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX3, 0))
2625       return 0;
2626 
2627     n_clients = ntohl(reply.data.client_accesses_by_index.n_clients);
2628     n_indices = ntohl(reply.data.client_accesses_by_index.n_indices);
2629 
2630     for (i = 0; i < n_clients && i < MAX_CLIENT_ACCESSES; i++) {
2631       client = &reply.data.client_accesses_by_index.clients[i];
2632 
2633       UTI_IPNetworkToHost(&client->ip, &ip);
2634 
2635       /* UNSPEC means the record could not be found in the daemon's tables.
2636          We shouldn't ever generate this case, but ignore it if we do. */
2637       if (ip.family == IPADDR_UNSPEC)
2638         continue;
2639 
2640       format_name(name, sizeof (name), 25, 0, 0, 0, &ip);
2641 
2642       print_report("%-25s  %6U  %5U  %C  %C  %I  %6U  %5U  %C  %I\n",
2643                    name,
2644                    (unsigned long)ntohl(client->ntp_hits),
2645                    (unsigned long)ntohl(client->ntp_drops),
2646                    client->ntp_interval,
2647                    client->ntp_timeout_interval,
2648                    (unsigned long)ntohl(client->last_ntp_hit_ago),
2649                    (unsigned long)ntohl(nke ? client->nke_hits : client->cmd_hits),
2650                    (unsigned long)ntohl(nke ? client->nke_drops : client->cmd_drops),
2651                    nke ? client->nke_interval : client->cmd_interval,
2652                    (unsigned long)ntohl(nke ? client->last_nke_hit_ago :
2653                                               client->last_cmd_hit_ago),
2654                    REPORT_END);
2655     }
2656 
2657     /* Set the next index to probe based on what the server tells us */
2658     next_index = ntohl(reply.data.client_accesses_by_index.next_index);
2659 
2660     if (next_index >= n_indices || n_clients < MAX_CLIENT_ACCESSES)
2661       break;
2662   }
2663 
2664   return 1;
2665 }
2666 
2667 
2668 /* ================================================== */
2669 /* Process the manual list command */
2670 static int
process_cmd_manual_list(const char * line)2671 process_cmd_manual_list(const char *line)
2672 {
2673   CMD_Request request;
2674   CMD_Reply reply;
2675   uint32_t i, n_samples;
2676   RPY_ManualListSample *sample;
2677   struct timespec when;
2678 
2679   request.command = htons(REQ_MANUAL_LIST);
2680   if (!request_reply(&request, &reply, RPY_MANUAL_LIST2, 0))
2681     return 0;
2682 
2683   n_samples = ntohl(reply.data.manual_list.n_samples);
2684   print_info_field("210 n_samples = %lu\n", (unsigned long)n_samples);
2685 
2686   print_header("#    Date     Time(UTC)    Slewed   Original   Residual");
2687 
2688   for (i = 0; i < n_samples && i < MAX_MANUAL_LIST_SAMPLES; i++) {
2689     sample = &reply.data.manual_list.samples[i];
2690     UTI_TimespecNetworkToHost(&sample->when, &when);
2691 
2692     print_report("%2d %s %10.2f %10.2f %10.2f\n",
2693                  i, UTI_TimeToLogForm(when.tv_sec),
2694                  UTI_FloatNetworkToHost(sample->slewed_offset),
2695                  UTI_FloatNetworkToHost(sample->orig_offset),
2696                  UTI_FloatNetworkToHost(sample->residual),
2697                  REPORT_END);
2698   }
2699 
2700   return 1;
2701 }
2702 
2703 /* ================================================== */
2704 
2705 static int
process_cmd_manual_delete(CMD_Request * msg,const char * line)2706 process_cmd_manual_delete(CMD_Request *msg, const char *line)
2707 {
2708   int index;
2709 
2710   if (sscanf(line, "%d", &index) != 1) {
2711     LOG(LOGS_ERR, "Bad syntax for manual delete command");
2712     return 0;
2713   }
2714 
2715   msg->command = htons(REQ_MANUAL_DELETE);
2716   msg->data.manual_delete.index = htonl(index);
2717   return 1;
2718 }
2719 
2720 /* ================================================== */
2721 
2722 static int
process_cmd_settime(char * line)2723 process_cmd_settime(char *line)
2724 {
2725   struct timespec ts;
2726   time_t now, new_time;
2727   CMD_Request request;
2728   CMD_Reply reply;
2729   double dfreq_ppm, new_afreq_ppm;
2730   double offset;
2731 
2732   now = time(NULL);
2733   new_time = get_date(line, &now);
2734 
2735   if (new_time == -1) {
2736     printf("510 - Could not parse date string\n");
2737   } else {
2738     ts.tv_sec = new_time;
2739     ts.tv_nsec = 0;
2740     UTI_TimespecHostToNetwork(&ts, &request.data.settime.ts);
2741     request.command = htons(REQ_SETTIME);
2742     if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP2, 1)) {
2743           offset = UTI_FloatNetworkToHost(reply.data.manual_timestamp.offset);
2744           dfreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.dfreq_ppm);
2745           new_afreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.new_afreq_ppm);
2746           printf("Clock was %.2f seconds fast.  Frequency change = %.2fppm, new frequency = %.2fppm\n",
2747               offset, dfreq_ppm, new_afreq_ppm);
2748           return 1;
2749     }
2750   }
2751   return 0;
2752 }
2753 
2754 /* ================================================== */
2755 
2756 static void
process_cmd_rekey(CMD_Request * msg,char * line)2757 process_cmd_rekey(CMD_Request *msg, char *line)
2758 {
2759   msg->command = htons(REQ_REKEY);
2760 }
2761 
2762 /* ================================================== */
2763 
2764 static int
process_cmd_makestep(CMD_Request * msg,char * line)2765 process_cmd_makestep(CMD_Request *msg, char *line)
2766 {
2767   int limit;
2768   double threshold;
2769 
2770   if (*line) {
2771     if (sscanf(line, "%lf %d", &threshold, &limit) != 2) {
2772       LOG(LOGS_ERR, "Bad syntax for makestep command");
2773       return 0;
2774     }
2775     msg->command = htons(REQ_MODIFY_MAKESTEP);
2776     msg->data.modify_makestep.limit = htonl(limit);
2777     msg->data.modify_makestep.threshold = UTI_FloatHostToNetwork(threshold);
2778   } else {
2779     msg->command = htons(REQ_MAKESTEP);
2780   }
2781 
2782   return 1;
2783 }
2784 
2785 /* ================================================== */
2786 
2787 static int
process_cmd_activity(const char * line)2788 process_cmd_activity(const char *line)
2789 {
2790   CMD_Request request;
2791   CMD_Reply reply;
2792 
2793   request.command = htons(REQ_ACTIVITY);
2794   if (!request_reply(&request, &reply, RPY_ACTIVITY, 0))
2795     return 0;
2796 
2797   print_info_field("200 OK\n");
2798 
2799   print_report("%U sources online\n"
2800                "%U sources offline\n"
2801                "%U sources doing burst (return to online)\n"
2802                "%U sources doing burst (return to offline)\n"
2803                "%U sources with unknown address\n",
2804                (unsigned long)ntohl(reply.data.activity.online),
2805                (unsigned long)ntohl(reply.data.activity.offline),
2806                (unsigned long)ntohl(reply.data.activity.burst_online),
2807                (unsigned long)ntohl(reply.data.activity.burst_offline),
2808                (unsigned long)ntohl(reply.data.activity.unresolved),
2809                REPORT_END);
2810 
2811   return 1;
2812 }
2813 
2814 /* ================================================== */
2815 
2816 static int
process_cmd_reselectdist(CMD_Request * msg,char * line)2817 process_cmd_reselectdist(CMD_Request *msg, char *line)
2818 {
2819   double dist;
2820   int ok;
2821   msg->command = htons(REQ_RESELECTDISTANCE);
2822   if (sscanf(line, "%lf", &dist) == 1) {
2823     msg->data.reselect_distance.distance = UTI_FloatHostToNetwork(dist);
2824     ok = 1;
2825   } else {
2826     ok = 0;
2827   }
2828   return ok;
2829 }
2830 
2831 /* ================================================== */
2832 
2833 static void
process_cmd_reselect(CMD_Request * msg,char * line)2834 process_cmd_reselect(CMD_Request *msg, char *line)
2835 {
2836   msg->command = htons(REQ_RESELECT);
2837 }
2838 
2839 /* ================================================== */
2840 
2841 static void
process_cmd_refresh(CMD_Request * msg,char * line)2842 process_cmd_refresh(CMD_Request *msg, char *line)
2843 {
2844   msg->command = htons(REQ_REFRESH);
2845 }
2846 
2847 /* ================================================== */
2848 
2849 static void
process_cmd_shutdown(CMD_Request * msg,char * line)2850 process_cmd_shutdown(CMD_Request *msg, char *line)
2851 {
2852   msg->command = htons(REQ_SHUTDOWN);
2853 }
2854 
2855 /* ================================================== */
2856 
2857 static int
process_cmd_reload(CMD_Request * msg,char * line)2858 process_cmd_reload(CMD_Request *msg, char *line)
2859 {
2860   if (!strcmp(line, "sources")) {
2861     msg->command = htons(REQ_RELOAD_SOURCES);
2862   } else {
2863     LOG(LOGS_ERR, "Invalid syntax for reload command");
2864     return 0;
2865   }
2866 
2867   return 1;
2868 }
2869 
2870 /* ================================================== */
2871 
2872 static int
process_cmd_reset(CMD_Request * msg,char * line)2873 process_cmd_reset(CMD_Request *msg, char *line)
2874 {
2875   if (!strcmp(line, "sources")) {
2876     msg->command = htons(REQ_RESET_SOURCES);
2877   } else {
2878     LOG(LOGS_ERR, "Invalid syntax for reset command");
2879     return 0;
2880   }
2881 
2882   return 1;
2883 }
2884 
2885 /* ================================================== */
2886 
2887 static int
process_cmd_waitsync(char * line)2888 process_cmd_waitsync(char *line)
2889 {
2890   CMD_Request request;
2891   CMD_Reply reply;
2892   IPAddr ip_addr;
2893   uint32_t ref_id;
2894   double correction, skew_ppm, max_correction, max_skew_ppm, interval;
2895   int ret = 0, max_tries, i;
2896   struct timeval timeout;
2897 
2898   max_tries = 0;
2899   max_correction = 0.0;
2900   max_skew_ppm = 0.0;
2901   interval = 10.0;
2902 
2903   if (sscanf(line, "%d %lf %lf %lf", &max_tries, &max_correction, &max_skew_ppm, &interval))
2904     ;
2905 
2906   /* Don't allow shorter interval than 0.1 seconds */
2907   if (interval < 0.1)
2908     interval = 0.1;
2909 
2910   request.command = htons(REQ_TRACKING);
2911 
2912   for (i = 1; ; i++) {
2913     if (request_reply(&request, &reply, RPY_TRACKING, 0)) {
2914       ref_id = ntohl(reply.data.tracking.ref_id);
2915       UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr);
2916 
2917       correction = UTI_FloatNetworkToHost(reply.data.tracking.current_correction);
2918       correction = fabs(correction);
2919       skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm);
2920 
2921       print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n",
2922                    i, (unsigned long)ref_id, correction, skew_ppm, REPORT_END);
2923 
2924       if ((ip_addr.family != IPADDR_UNSPEC ||
2925            (ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) &&
2926           (max_correction == 0.0 || correction <= max_correction) &&
2927           (max_skew_ppm == 0.0 || skew_ppm <= max_skew_ppm)) {
2928         ret = 1;
2929       }
2930     }
2931 
2932     if (!ret && (!max_tries || i < max_tries) && !quit) {
2933       UTI_DoubleToTimeval(interval, &timeout);
2934       if (select(0, NULL, NULL, NULL, &timeout))
2935         break;
2936     } else {
2937       break;
2938     }
2939   }
2940   return ret;
2941 }
2942 
2943 /* ================================================== */
2944 
2945 static int
process_cmd_dns(const char * line)2946 process_cmd_dns(const char *line)
2947 {
2948   if (!strcmp(line, "-46")) {
2949     DNS_SetAddressFamily(IPADDR_UNSPEC);
2950   } else if (!strcmp(line, "-4")) {
2951     DNS_SetAddressFamily(IPADDR_INET4);
2952   } else if (!strcmp(line, "-6")) {
2953     DNS_SetAddressFamily(IPADDR_INET6);
2954   } else if (!strcmp(line, "-n")) {
2955     no_dns = 1;
2956   } else if (!strcmp(line, "+n")) {
2957     no_dns = 0;
2958   } else {
2959     LOG(LOGS_ERR, "Unrecognized dns command");
2960     return 0;
2961   }
2962   return 1;
2963 }
2964 
2965 /* ================================================== */
2966 
2967 static int
process_cmd_timeout(const char * line)2968 process_cmd_timeout(const char *line)
2969 {
2970   int timeout;
2971 
2972   timeout = atoi(line);
2973   if (timeout < 100) {
2974     LOG(LOGS_ERR, "Timeout %d is too short", timeout);
2975     return 0;
2976   }
2977   initial_timeout = timeout;
2978   return 1;
2979 }
2980 
2981 /* ================================================== */
2982 
2983 static int
process_cmd_retries(const char * line)2984 process_cmd_retries(const char *line)
2985 {
2986   int retries;
2987 
2988   retries = atoi(line);
2989   if (retries < 0 || retries > 30) {
2990     LOG(LOGS_ERR, "Invalid maximum number of retries");
2991     return 0;
2992   }
2993   max_retries = retries;
2994   return 1;
2995 }
2996 
2997 /* ================================================== */
2998 
2999 static int
process_cmd_keygen(char * line)3000 process_cmd_keygen(char *line)
3001 {
3002   unsigned int i, args, cmac_length, length, id = 1, bits = 160;
3003   unsigned char key[512];
3004   const char *type;
3005   char *words[3];
3006 
3007 #ifdef FEAT_SECHASH
3008   type = "SHA1";
3009 #else
3010   type = "MD5";
3011 #endif
3012 
3013   args = UTI_SplitString(line, words, 3);
3014   if (args >= 2)
3015     type = words[1];
3016 
3017   if (args > 3 ||
3018       (args >= 1 && sscanf(words[0], "%u", &id) != 1) ||
3019       (args >= 3 && sscanf(words[2], "%u", &bits) != 1)) {
3020     LOG(LOGS_ERR, "Invalid syntax for keygen command");
3021     return 0;
3022   }
3023 
3024 #ifdef HAVE_CMAC
3025   cmac_length = CMC_GetKeyLength(UTI_CmacNameToAlgorithm(type));
3026 #else
3027   cmac_length = 0;
3028 #endif
3029 
3030   if (HSH_GetHashId(UTI_HashNameToAlgorithm(type)) >= 0) {
3031     length = (bits + 7) / 8;
3032   } else if (cmac_length > 0) {
3033     length = cmac_length;
3034   } else {
3035     LOG(LOGS_ERR, "Unknown hash function or cipher %s", type);
3036     return 0;
3037   }
3038 
3039   length = CLAMP(10, length, sizeof (key));
3040 
3041   UTI_GetRandomBytesUrandom(key, length);
3042 
3043   printf("%u %s HEX:", id, type);
3044   for (i = 0; i < length; i++)
3045     printf("%02hhX", key[i]);
3046   printf("\n");
3047 
3048   return 1;
3049 }
3050 
3051 /* ================================================== */
3052 
3053 static int
process_line(char * line)3054 process_line(char *line)
3055 {
3056   char *command;
3057   int do_normal_submit;
3058   int ret;
3059   CMD_Request tx_message;
3060   CMD_Reply rx_message;
3061 
3062   ret = 0;
3063 
3064   do_normal_submit = 1;
3065 
3066   CPS_NormalizeLine(line);
3067 
3068   if (!*line) {
3069     fflush(stderr);
3070     fflush(stdout);
3071     return 1;
3072   };
3073 
3074   command = line;
3075   line = CPS_SplitWord(line);
3076 
3077   if (!strcmp(command, "accheck")) {
3078     do_normal_submit = process_cmd_accheck(&tx_message, line);
3079   } else if (!strcmp(command, "activity")) {
3080     do_normal_submit = 0;
3081     ret = process_cmd_activity(line);
3082   } else if (!strcmp(command, "add")) {
3083     do_normal_submit = process_cmd_add_source(&tx_message, line);
3084   } else if (!strcmp(command, "allow")) {
3085     do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_ALLOW, REQ_ALLOWALL);
3086   } else if (!strcmp(command, "authdata")) {
3087     do_normal_submit = 0;
3088     ret = process_cmd_authdata(line);
3089   } else if (!strcmp(command, "burst")) {
3090     do_normal_submit = process_cmd_burst(&tx_message, line);
3091   } else if (!strcmp(command, "clients")) {
3092     ret = process_cmd_clients(line);
3093     do_normal_submit = 0;
3094   } else if (!strcmp(command, "cmdaccheck")) {
3095     do_normal_submit = process_cmd_cmdaccheck(&tx_message, line);
3096   } else if (!strcmp(command, "cmdallow")) {
3097     do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDALLOW, REQ_CMDALLOWALL);
3098   } else if (!strcmp(command, "cmddeny")) {
3099     do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDDENY, REQ_CMDDENYALL);
3100   } else if (!strcmp(command, "cyclelogs")) {
3101     process_cmd_cyclelogs(&tx_message, line);
3102   } else if (!strcmp(command, "delete")) {
3103     do_normal_submit = process_cmd_delete(&tx_message, line);
3104   } else if (!strcmp(command, "deny")) {
3105     do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_DENY, REQ_DENYALL);
3106   } else if (!strcmp(command, "dfreq")) {
3107     do_normal_submit = process_cmd_dfreq(&tx_message, line);
3108   } else if (!strcmp(command, "dns")) {
3109     ret = process_cmd_dns(line);
3110     do_normal_submit = 0;
3111   } else if (!strcmp(command, "doffset")) {
3112     do_normal_submit = process_cmd_doffset(&tx_message, line);
3113   } else if (!strcmp(command, "dump")) {
3114     process_cmd_dump(&tx_message, line);
3115   } else if (!strcmp(command, "exit")) {
3116     do_normal_submit = 0;
3117     quit = 1;
3118     ret = 1;
3119   } else if (!strcmp(command, "help")) {
3120     do_normal_submit = 0;
3121     give_help();
3122     ret = 1;
3123   } else if (!strcmp(command, "keygen")) {
3124     ret = process_cmd_keygen(line);
3125     do_normal_submit = 0;
3126   } else if (!strcmp(command, "local")) {
3127     do_normal_submit = process_cmd_local(&tx_message, line);
3128   } else if (!strcmp(command, "makestep")) {
3129     do_normal_submit = process_cmd_makestep(&tx_message, line);
3130   } else if (!strcmp(command, "manual")) {
3131     if (!strncmp(line, "list", 4)) {
3132       do_normal_submit = 0;
3133       ret = process_cmd_manual_list(CPS_SplitWord(line));
3134     } else if (!strncmp(line, "delete", 6)) {
3135       do_normal_submit = process_cmd_manual_delete(&tx_message, CPS_SplitWord(line));
3136     } else {
3137       do_normal_submit = process_cmd_manual(&tx_message, line);
3138     }
3139   } else if (!strcmp(command, "maxdelay")) {
3140     do_normal_submit = process_cmd_maxdelay(&tx_message, line);
3141   } else if (!strcmp(command, "maxdelaydevratio")) {
3142     do_normal_submit = process_cmd_maxdelaydevratio(&tx_message, line);
3143   } else if (!strcmp(command, "maxdelayratio")) {
3144     do_normal_submit = process_cmd_maxdelayratio(&tx_message, line);
3145   } else if (!strcmp(command, "maxpoll")) {
3146     do_normal_submit = process_cmd_maxpoll(&tx_message, line);
3147   } else if (!strcmp(command, "maxupdateskew")) {
3148     do_normal_submit = process_cmd_maxupdateskew(&tx_message, line);
3149   } else if (!strcmp(command, "minpoll")) {
3150     do_normal_submit = process_cmd_minpoll(&tx_message, line);
3151   } else if (!strcmp(command, "minstratum")) {
3152     do_normal_submit = process_cmd_minstratum(&tx_message, line);
3153   } else if (!strcmp(command, "ntpdata")) {
3154     do_normal_submit = 0;
3155     ret = process_cmd_ntpdata(line);
3156   } else if (!strcmp(command, "offline")) {
3157     do_normal_submit = process_cmd_offline(&tx_message, line);
3158   } else if (!strcmp(command, "online")) {
3159     do_normal_submit = process_cmd_online(&tx_message, line);
3160   } else if (!strcmp(command, "onoffline")) {
3161     process_cmd_onoffline(&tx_message, line);
3162   } else if (!strcmp(command, "polltarget")) {
3163     do_normal_submit = process_cmd_polltarget(&tx_message, line);
3164   } else if (!strcmp(command, "quit")) {
3165     do_normal_submit = 0;
3166     quit = 1;
3167     ret = 1;
3168   } else if (!strcmp(command, "refresh")) {
3169     process_cmd_refresh(&tx_message, line);
3170   } else if (!strcmp(command, "rekey")) {
3171     process_cmd_rekey(&tx_message, line);
3172   } else if (!strcmp(command, "reload")) {
3173     do_normal_submit = process_cmd_reload(&tx_message, line);
3174   } else if (!strcmp(command, "reselect")) {
3175     process_cmd_reselect(&tx_message, line);
3176   } else if (!strcmp(command, "reselectdist")) {
3177     do_normal_submit = process_cmd_reselectdist(&tx_message, line);
3178   } else if (!strcmp(command, "reset")) {
3179     do_normal_submit = process_cmd_reset(&tx_message, line);
3180   } else if (!strcmp(command, "retries")) {
3181     ret = process_cmd_retries(line);
3182     do_normal_submit = 0;
3183   } else if (!strcmp(command, "rtcdata")) {
3184     do_normal_submit = 0;
3185     ret = process_cmd_rtcreport(line);
3186   } else if (!strcmp(command, "selectdata")) {
3187     do_normal_submit = 0;
3188     ret = process_cmd_selectdata(line);
3189   } else if (!strcmp(command, "serverstats")) {
3190     do_normal_submit = 0;
3191     ret = process_cmd_serverstats(line);
3192   } else if (!strcmp(command, "settime")) {
3193     do_normal_submit = 0;
3194     ret = process_cmd_settime(line);
3195   } else if (!strcmp(command, "shutdown")) {
3196     process_cmd_shutdown(&tx_message, line);
3197   } else if (!strcmp(command, "smoothing")) {
3198     do_normal_submit = 0;
3199     ret = process_cmd_smoothing(line);
3200   } else if (!strcmp(command, "smoothtime")) {
3201     do_normal_submit = process_cmd_smoothtime(&tx_message, line);
3202   } else if (!strcmp(command, "sourcename")) {
3203     do_normal_submit = 0;
3204     ret = process_cmd_sourcename(line);
3205   } else if (!strcmp(command, "sources")) {
3206     do_normal_submit = 0;
3207     ret = process_cmd_sources(line);
3208   } else if (!strcmp(command, "sourcestats")) {
3209     do_normal_submit = 0;
3210     ret = process_cmd_sourcestats(line);
3211   } else if (!strcmp(command, "timeout")) {
3212     ret = process_cmd_timeout(line);
3213     do_normal_submit = 0;
3214   } else if (!strcmp(command, "tracking")) {
3215     ret = process_cmd_tracking(line);
3216     do_normal_submit = 0;
3217   } else if (!strcmp(command, "trimrtc")) {
3218     process_cmd_trimrtc(&tx_message, line);
3219   } else if (!strcmp(command, "waitsync")) {
3220     ret = process_cmd_waitsync(line);
3221     do_normal_submit = 0;
3222   } else if (!strcmp(command, "writertc")) {
3223     process_cmd_writertc(&tx_message, line);
3224   } else if (!strcmp(command, "authhash") ||
3225              !strcmp(command, "password")) {
3226     /* Warn, but don't return error to not break scripts */
3227     LOG(LOGS_WARN, "Authentication is no longer supported");
3228     do_normal_submit = 0;
3229     ret = 1;
3230   } else {
3231     LOG(LOGS_ERR, "Unrecognized command");
3232     do_normal_submit = 0;
3233   }
3234 
3235   if (do_normal_submit) {
3236     ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
3237   }
3238   fflush(stderr);
3239   fflush(stdout);
3240   return ret;
3241 }
3242 
3243 /* ================================================== */
3244 
3245 static int
process_args(int argc,char ** argv,int multi)3246 process_args(int argc, char **argv, int multi)
3247 {
3248   int total_length, i, ret = 0;
3249   char *line;
3250 
3251   total_length = 0;
3252   for(i=0; i<argc; i++) {
3253     total_length += strlen(argv[i]) + 1;
3254   }
3255 
3256   line = (char *) Malloc((2 + total_length) * sizeof(char));
3257 
3258   for (i = 0; i < argc; i++) {
3259     line[0] = '\0';
3260     if (multi) {
3261       strcat(line, argv[i]);
3262     } else {
3263       for (; i < argc; i++) {
3264         strcat(line, argv[i]);
3265         if (i + 1 < argc)
3266           strcat(line, " ");
3267       }
3268     }
3269 
3270     ret = process_line(line);
3271     if (!ret || quit)
3272       break;
3273   }
3274 
3275   Free(line);
3276 
3277   return ret;
3278 }
3279 
3280 /* ================================================== */
3281 
3282 static void
signal_handler(int signum)3283 signal_handler(int signum)
3284 {
3285   quit = 1;
3286 }
3287 
3288 /* ================================================== */
3289 
3290 static void
display_gpl(void)3291 display_gpl(void)
3292 {
3293     printf("chrony version %s\n"
3294            "Copyright (C) 1997-2003, 2007, 2009-2021 Richard P. Curnow and others\n"
3295            "chrony comes with ABSOLUTELY NO WARRANTY.  This is free software, and\n"
3296            "you are welcome to redistribute it under certain conditions.  See the\n"
3297            "GNU General Public License version 2 for details.\n\n",
3298            CHRONY_VERSION);
3299 }
3300 
3301 /* ================================================== */
3302 
3303 static void
print_help(const char * progname)3304 print_help(const char *progname)
3305 {
3306       printf("Usage: %s [OPTION]... [COMMAND]...\n\n"
3307              "Options:\n"
3308              "  -4\t\tUse IPv4 addresses only\n"
3309              "  -6\t\tUse IPv6 addresses only\n"
3310              "  -n\t\tDon't resolve hostnames\n"
3311              "  -N\t\tPrint original source names\n"
3312              "  -c\t\tEnable CSV format\n"
3313 #if DEBUG > 0
3314              "  -d\t\tEnable debug messages\n"
3315 #endif
3316              "  -m\t\tAccept multiple commands\n"
3317              "  -h HOST\tSpecify server (%s)\n"
3318              "  -p PORT\tSpecify UDP port (%d)\n"
3319              "  -v, --version\tPrint version and exit\n"
3320              "      --help\tPrint usage and exit\n",
3321              progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1", DEFAULT_CANDM_PORT);
3322 }
3323 
3324 /* ================================================== */
3325 
3326 static void
print_version(void)3327 print_version(void)
3328 {
3329       printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES);
3330 }
3331 
3332 /* ================================================== */
3333 
3334 int
main(int argc,char ** argv)3335 main(int argc, char **argv)
3336 {
3337   char *line;
3338   const char *progname = argv[0];
3339   const char *hostnames = NULL;
3340   int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
3341   int port = DEFAULT_CANDM_PORT;
3342 
3343   /* Parse long command-line options */
3344   for (optind = 1; optind < argc; optind++) {
3345     if (!strcmp("--help", argv[optind])) {
3346       print_help(progname);
3347       return 0;
3348     } else if (!strcmp("--version", argv[optind])) {
3349       print_version();
3350       return 0;
3351     }
3352   }
3353 
3354   optind = 1;
3355 
3356   /* Parse short command-line options */
3357   while ((opt = getopt(argc, argv, "+46acdf:h:mnNp:v")) != -1) {
3358     switch (opt) {
3359       case '4':
3360       case '6':
3361         family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
3362         break;
3363       case 'a':
3364       case 'f':
3365         /* For compatibility only */
3366         break;
3367       case 'c':
3368         csv_mode = 1;
3369         break;
3370       case 'd':
3371 #if DEBUG > 0
3372         log_min_severity = LOGS_DEBUG;
3373 #endif
3374         break;
3375       case 'h':
3376         hostnames = optarg;
3377         break;
3378       case 'm':
3379         multi = 1;
3380         break;
3381       case 'n':
3382         no_dns = 1;
3383         break;
3384       case 'N':
3385         source_names = 1;
3386         break;
3387       case 'p':
3388         port = atoi(optarg);
3389         break;
3390       case 'v':
3391         print_version();
3392         return 0;
3393       default:
3394         print_help(progname);
3395         return 1;
3396     }
3397   }
3398 
3399   if (isatty(0) && isatty(1) && isatty(2)) {
3400     on_terminal = 1;
3401   }
3402 
3403   if (on_terminal && optind == argc) {
3404     display_gpl();
3405   }
3406 
3407   DNS_SetAddressFamily(family);
3408 
3409   if (!hostnames) {
3410     hostnames = DEFAULT_COMMAND_SOCKET",127.0.0.1,::1";
3411   }
3412 
3413   UTI_SetQuitSignalsHandler(signal_handler, 0);
3414 
3415   SCK_Initialise(IPADDR_UNSPEC);
3416   server_addresses = get_addresses(hostnames, port);
3417 
3418   if (!open_io())
3419     LOG_FATAL("Could not open connection to daemon");
3420 
3421   if (optind < argc) {
3422     ret = process_args(argc - optind, argv + optind, multi);
3423   } else {
3424     do {
3425       line = read_line();
3426       if (line && !quit) {
3427         ret = process_line(line);
3428       }else {
3429 	/* supply the final '\n' when user exits via ^D */
3430         if( on_terminal ) printf("\n");
3431       }
3432     } while (line && !quit);
3433   }
3434 
3435   close_io();
3436   free_addresses(server_addresses);
3437   SCK_Finalise();
3438 
3439   return !ret;
3440 }
3441 
3442 
3443