1 /*        $NetBSD: libntpq.c,v 1.6 2020/05/25 20:47:26 christos Exp $ */
2 
3 /*****************************************************************************
4  *
5  *  libntpq.c
6  *
7  *  This is the wrapper library for ntpq, the NTP query utility.
8  *  This library reuses the sourcecode from ntpq and exports a number
9  *  of useful functions in a library that can be linked against applications
10  *  that need to query the status of a running ntpd. The whole
11  *  communcation is based on mode 6 packets.
12  *
13  ****************************************************************************/
14 #define LIBNTPQ_C
15 #define NO_MAIN_ALLOWED 1
16 /* #define BUILD_AS_LIB                 Already provided by the Makefile */
17 
18 #include "ntpq.c"
19 #include "libntpq.h"
20 
21 /* Function Prototypes */
22 
23 
24 const char *Version = "libntpq 0.3beta";
25 
26 /* global variables used for holding snapshots of data */
27 char peervars[NTPQ_BUFLEN];
28 int peervarlen = 0;
29 associd_t peervar_assoc = 0;
30 char clockvars[NTPQ_BUFLEN];
31 int clockvarlen = 0;
32 int clockvar_assoc = 0;
33 char sysvars[NTPQ_BUFLEN];
34 int sysvarlen = 0;
35 char *ntpq_resultbuffer[NTPQ_BUFLEN];
36 unsigned short ntpq_associations[MAXASSOC];
37 struct ntpq_varlist ntpq_varlist[MAXLIST];
38 
39 /*****************************************************************************
40  *
41  *  ntpq_stripquotes
42  *
43  *  Parses a given character buffer srcbuf and removes all quoted
44  *  characters. The resulting string is copied to the specified
45  *  resultbuf character buffer.  E.g. \" will be translated into "
46  *
47  ****************************************************************************
48  * Parameters:
49  *        resultbuf char*     The resulting string without quoted
50  *                                      characters
51  *        srcbuf              char*     The buffer holding the original string
52  *        datalen             int       The number of bytes stored in srcbuf
53  *        maxlen              int       Max. number of bytes for resultbuf
54  *
55  * Returns:
56  *        int                 number of chars that have been copied to
57  *                            resultbuf
58  ****************************************************************************/
59 
ntpq_stripquotes(char * resultbuf,char * srcbuf,int datalen,int maxlen)60 int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen )
61 {
62           char* dst = resultbuf;
63           char* dep = resultbuf + maxlen - 1;
64           char* src = srcbuf;
65           char* sep = srcbuf + (datalen >= 0 ? datalen : 0);
66           int   esc = 0;
67           int   ch;
68 
69           if (maxlen <= 0)
70                     return 0;
71 
72           while ((dst != dep) && (src != sep) && (ch = (u_char)*src++) != 0) {
73                     if (esc) {
74                               esc = 0;
75                               switch (ch) {
76                                         /* skip and do not copy */
77                                         /* case '"':*/ /* quotes */
78                               case 'n': /*newline*/
79                               case 'r': /*carriage return*/
80                               case 'g': /*bell*/
81                               case 't': /*tab*/
82                                         continue;
83                               default:
84                                         break;
85                               }
86                     } else {
87                               switch (ch) {
88                               case '\\':
89                                         esc = 1;
90                               case '"':
91                                         continue;
92                               default:
93                                         break;
94                               }
95                     }
96                     *dst++ = (char)ch;
97           }
98           *dst = '\0';
99           return (int)(dst - resultbuf);
100 }
101 
102 
103 /*****************************************************************************
104  *
105  *  ntpq_getvar
106  *
107  *  This function parses a given buffer for a variable/value pair and
108  *  copies the value of the requested variable into the specified
109  *  varvalue buffer.
110  *
111  *  It returns the number of bytes copied or zero for an empty result
112  *  (=no matching variable found or empty value)
113  *
114  ****************************************************************************
115  * Parameters:
116  *        resultbuf char*     The resulting string without quoted
117  *                                      characters
118  *        datalen             size_t    The number of bytes stored in
119  *                                                                    resultbuf
120  *        varname             char*     Name of the required variable
121  *        varvalue  char*     Where the value of the variable should
122  *                                                                    be stored
123  *        maxlen              size_t    Max. number of bytes for varvalue
124  *
125  * Returns:
126  *        size_t              number of chars that have been copied to
127  *                            varvalue
128  ****************************************************************************/
129 
130 size_t
ntpq_getvar(const char * resultbuf,size_t datalen,const char * varname,char * varvalue,size_t maxlen)131 ntpq_getvar(
132           const char *        resultbuf,
133           size_t              datalen,
134           const char *        varname,
135           char *              varvalue,
136           size_t              maxlen)
137 {
138           char *    name;
139           char *    value;
140           size_t    idatalen;
141 
142           value = NULL;
143           idatalen = (int)datalen;
144 
145           while (nextvar(&idatalen, &resultbuf, &name, &value)) {
146                     if (strcmp(varname, name) == 0) {
147                               ntpq_stripquotes(varvalue, value, strlen(value), maxlen);
148 
149                               return strlen(varvalue);
150                     }
151           }
152 
153           return 0;
154 }
155 
156 
157 /*****************************************************************************
158  *
159  *  ntpq_queryhost
160  *
161  *  Sends a mode 6 query packet to the current open host (see
162  *  ntpq_openhost) and stores the requested variable set in the specified
163  *  character buffer.
164  *  It returns the number of bytes read or zero for an empty result
165  *  (=no answer or empty value)
166  *
167  ****************************************************************************
168  * Parameters:
169  *      VARSET                u_short   Which variable set should be
170  *                                      read (PEERVARS or CLOCKVARS)
171  *        association         int       The association ID that should be read
172  *                                      0 represents the ntpd instance itself
173  *        resultbuf char*     The resulting string without quoted
174  *                                      characters
175  *        maxlen              int       Max. number of bytes for varvalue
176  *
177  * Returns:
178  *        int                 number of bytes that have been copied to
179  *                            resultbuf
180  *                            - OR -
181  *                            0 (zero) if no reply has been received or
182  *                            another failure occured
183  ****************************************************************************/
184 
ntpq_queryhost(unsigned short VARSET,unsigned short association,char * resultbuf,int maxlen)185 int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen)
186 {
187           const char *datap;
188           int res;
189           size_t    dsize;
190           u_short   rstatus;
191 
192           if ( numhosts > 0 )
193                     res = doquery(VARSET,association,0,0, (char *)0, &rstatus, &dsize, &datap);
194           else
195                     return 0;
196 
197           if ( ( res != 0) || ( dsize == 0 ) ) /* no data */
198                     return 0;
199 
200           if ( dsize > maxlen)
201                     dsize = maxlen;
202 
203 
204           /* fill result resultbuf */
205           memcpy(resultbuf, datap, dsize);
206 
207           return dsize;
208 }
209 
210 
211 
212 /*****************************************************************************
213  *
214  *  ntpq_openhost
215  *
216  *  Sets up a connection to the ntpd instance of a specified host. Note:
217  *  There is no real "connection" established because NTP solely works
218  *  based on UDP.
219  *
220  ****************************************************************************
221  * Parameters:
222  *        hostname  char*     Hostname/IP of the host running ntpd
223  *        fam                 int       Address Family (AF_INET, AF_INET6, or 0)
224  *
225  * Returns:
226  *        int                 1 if the host connection could be set up, i.e.
227  *                            name resolution was succesful and/or IP address
228  *                            has been validated
229  *                            - OR -
230  *                            0 (zero) if a failure occured
231  ****************************************************************************/
232 
233 int
ntpq_openhost(char * hostname,int fam)234 ntpq_openhost(
235           char *hostname,
236           int fam
237           )
238 {
239           if ( openhost(hostname, fam) )
240           {
241                     numhosts = 1;
242           } else {
243                     numhosts = 0;
244           }
245 
246           return numhosts;
247 
248 }
249 
250 
251 /*****************************************************************************
252  *
253  *  ntpq_closehost
254  *
255  *  Cleans up a connection by closing the used socket. Should be called
256  *  when no further queries are required for the currently used host.
257  *
258  ****************************************************************************
259  * Parameters:
260  *        - none -
261  *
262  * Returns:
263  *        int                 0 (zero) if no host has been opened before
264  *                            - OR -
265  *                            the resultcode from the closesocket function call
266  ****************************************************************************/
267 
ntpq_closehost(void)268 int ntpq_closehost(void)
269 {
270           if ( numhosts )
271            return closesocket(sockfd);
272 
273           return 0;
274 }
275 
276 
277 /*****************************************************************************
278  *
279  *  ntpq_read_associations
280  *
281  *  This function queries the ntp host for its associations and returns the
282  *  number of associations found.
283  *
284  *  It takes an u_short array as its first parameter, this array holds the
285  *  IDs of the associations,
286  *  the function will not write more entries than specified with the
287  *  max_entries parameter.
288  *
289  *  However, if more than max_entries associations were found, the return
290  *  value of this function will reflect the real number, even if not all
291  *  associations have been stored in the array.
292  *
293  ****************************************************************************
294  * Parameters:
295  *        resultbuf u_short*Array that should hold the list of
296  *                                      association IDs
297  *        maxentries          int       maximum number of association IDs that can
298  *                                      be stored in resultbuf
299  *
300  * Returns:
301  *        int                 number of association IDs stored in resultbuf
302  *                            - OR -
303  *                            0 (zero) if a failure occured or no association has
304  *                            been returned.
305  ****************************************************************************/
306 
ntpq_read_associations(u_short resultbuf[],int max_entries)307  int  ntpq_read_associations ( u_short resultbuf[], int max_entries )
308 {
309     int i = 0;
310 
311     if (ntpq_dogetassoc()) {
312 
313         if(numassoc < max_entries)
314           max_entries = numassoc;
315 
316         for (i=0;i<max_entries;i++)
317             resultbuf[i] = assoc_cache[i].assid;
318 
319         return numassoc;
320     }
321 
322     return 0;
323 }
324 
325 
326 
327 
328 /*****************************************************************************
329  *
330  *  ntpq_get_assocs
331  *
332  *  This function reads the associations of a previously selected (with
333  *  ntpq_openhost) NTP host into its own (global) array and returns the
334  *  number of associations found.
335  *
336  *  The obtained association IDs can be read by using the ntpq_get_assoc_id
337  *  function.
338  *
339  ****************************************************************************
340  * Parameters:
341  *        - none -
342  *
343  * Returns:
344  *        int                 number of association IDs stored in resultbuf
345  *                            - OR -
346  *                            0 (zero) if a failure occured or no association has
347  *                            been returned.
348  ****************************************************************************/
349 
ntpq_get_assocs(void)350  int  ntpq_get_assocs ( void )
351 {
352     return ntpq_read_associations( ntpq_associations, MAXASSOC );
353 }
354 
355 
356 /*****************************************************************************
357  *
358  *  ntpq_get_assoc_number
359  *
360  *  This function returns for a given Association ID the association number
361  *  in the internal association array, which is filled by the ntpq_get_assocs
362  *  function.
363  *
364  ****************************************************************************
365  * Parameters:
366  *        associd             int       requested associaton ID
367  *
368  * Returns:
369  *        int                 the number of the association array element that is
370  *                            representing the given association ID
371  *                            - OR -
372  *                            -1 if a failure occured or no matching association
373  *                            ID has been found
374  ****************************************************************************/
375 
ntpq_get_assoc_number(associd_t associd)376 int ntpq_get_assoc_number ( associd_t associd )
377 {
378           int i;
379 
380           for (i=0;i<numassoc;i++) {
381                     if (assoc_cache[i].assid == associd)
382                               return i;
383           }
384 
385           return -1;
386 
387 }
388 
389 
390 /*****************************************************************************
391  *
392  *  ntpq_read_assoc_peervars
393  *
394  *  This function reads the peervars variable-set of a specified association
395  *  from a NTP host and writes it to the result buffer specified, honoring
396  *  the maxsize limit.
397  *
398  *  It returns the number of bytes written or 0 when the variable-set is
399  *  empty or failed to read.
400  *
401  ****************************************************************************
402  * Parameters:
403  *        associd             int       requested associaton ID
404  *        resultbuf char*     character buffer where the variable set
405  *                                      should be stored
406  *        maxsize             int       the maximum number of bytes that can be
407  *                                      written to resultbuf
408  *
409  * Returns:
410  *        int                 number of chars that have been copied to
411  *                            resultbuf
412  *                            - OR -
413  *                            0 (zero) if an error occured
414  ****************************************************************************/
415 
416 int
ntpq_read_assoc_peervars(associd_t associd,char * resultbuf,int maxsize)417 ntpq_read_assoc_peervars(
418           associd_t associd,
419           char *              resultbuf,
420           int                 maxsize
421           )
422 {
423           const char *        datap;
424           int                 res;
425           size_t              dsize;
426           u_short             rstatus;
427 
428           res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
429                           &dsize, &datap);
430           if (res != 0)
431                     return 0;
432           if (dsize <= 0) {
433                     if (numhosts > 1)
434                               fprintf(stderr, "server=%s ", currenthost);
435                     fprintf(stderr,
436                               "***No information returned for association %d\n",
437                               associd);
438 
439                     return 0;
440           }
441           if (dsize > maxsize)
442                     dsize = maxsize;
443           memcpy(resultbuf, datap, dsize);
444 
445           return dsize;
446 }
447 
448 
449 
450 
451 /*****************************************************************************
452  *
453  *  ntpq_read_sysvars
454  *
455  *  This function reads the sysvars variable-set from a NTP host and writes it
456  *  to the result buffer specified, honoring the maxsize limit.
457  *
458  *  It returns the number of bytes written or 0 when the variable-set is empty
459  *  or could not be read.
460  *
461  ****************************************************************************
462  * Parameters:
463  *        resultbuf char*     character buffer where the variable set
464  *                                      should be stored
465  *        maxsize             int       the maximum number of bytes that can be
466  *                                      written to resultbuf
467  *
468  * Returns:
469  *        int                 number of chars that have been copied to
470  *                            resultbuf
471  *                            - OR -
472  *                            0 (zero) if an error occured
473  ****************************************************************************/
474 size_t
ntpq_read_sysvars(char * resultbuf,size_t maxsize)475 ntpq_read_sysvars(
476           char *    resultbuf,
477           size_t    maxsize
478           )
479 {
480           const char *        datap;
481           int                 res;
482           size_t              dsize;
483           u_short             rstatus;
484 
485           res = doquery(CTL_OP_READVAR, 0, 0, 0, NULL, &rstatus,
486                           &dsize, &datap);
487 
488           if (res != 0)
489                     return 0;
490 
491           if (dsize == 0) {
492                     if (numhosts > 1)
493                               fprintf(stderr, "server=%s ", currenthost);
494                     fprintf(stderr, "***No sysvar information returned\n");
495 
496                     return 0;
497           } else {
498                     dsize = min(dsize, maxsize);
499                     memcpy(resultbuf, datap, dsize);
500           }
501 
502           return dsize;
503 }
504 
505 
506 /*****************************************************************************
507  *  ntpq_get_assoc_allvars
508  *
509  *  With this function all association variables for the specified association
510  *  ID can be requested from a NTP host. They are stored internally and can be
511  *  read by using the ntpq_get_peervar or ntpq_get_clockvar functions.
512  *
513  *  Basically this is only a combination of the ntpq_get_assoc_peervars and
514  *  ntpq_get_assoc_clockvars functions.
515  *
516  *  It returns 1 if both variable-sets (peervars and clockvars) were
517  *  received successfully. If one variable-set or both of them weren't
518  *  received,
519  *
520  ****************************************************************************
521  * Parameters:
522  *        associd             int       requested associaton ID
523  *
524  * Returns:
525  *        int                 nonzero if at least one variable set could be read
526  *                            - OR -
527  *                            0 (zero) if an error occured and both variable sets
528  *                            could not be read
529  ****************************************************************************/
ntpq_get_assoc_allvars(associd_t associd)530  int  ntpq_get_assoc_allvars( associd_t associd  )
531 {
532           return ntpq_get_assoc_peervars ( associd ) &
533                  ntpq_get_assoc_clockvars( associd );
534 }
535 
536 
537 
538 
539 /*****************************************************************************
540  *
541  *  ntpq_get_sysvars
542  *
543  *  The system variables of a NTP host can be requested by using this function
544  *  and afterwards using ntpq_get_sysvar to read the single variable values.
545  *
546  ****************************************************************************
547  * Parameters:
548  *        - none -
549  *
550  * Returns:
551  *        int                 nonzero if the variable set could be read
552  *                            - OR -
553  *                            0 (zero) if an error occured and the sysvars
554  *                            could not be read
555  ****************************************************************************/
556 int
ntpq_get_sysvars(void)557 ntpq_get_sysvars(void)
558 {
559           sysvarlen = ntpq_read_sysvars(sysvars, sizeof(sysvars));
560           if (sysvarlen <= 0)
561                     return 0;
562           else
563                     return 1;
564 }
565 
566 
567 /*****************************************************************************
568  *
569  *  ntp_get_peervar
570  *
571  *  This function uses the variable-set which was read by using
572  *  ntp_get_peervars and searches for a variable specified with varname. If
573  *  such a variable exists, it writes its value into
574  *  varvalue (maxlen specifies the size of this target buffer).
575  *
576  ****************************************************************************
577  * Parameters:
578  *        varname             char*     requested variable name
579  *        varvalue  char*     the buffer where the value should go into
580  *        maxlen              int       maximum number of bytes that can be copied to
581  *                                      varvalue
582  *
583  * Returns:
584  *        int                 number of bytes copied to varvalue
585  *                            - OR -
586  *                            0 (zero) if an error occured or the variable could
587  *                            not be found
588  ****************************************************************************/
ntpq_get_peervar(const char * varname,char * varvalue,int maxlen)589 int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen)
590 {
591     return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) );
592 }
593 
594 
595 
596 /*****************************************************************************
597  *
598  *  ntpq_get_assoc_peervars
599  *
600  *  This function requests the peer variables of the specified association
601  *  from a NTP host. In order to access the variable values, the function
602  *  ntpq_get_peervar must be used.
603  *
604  ****************************************************************************
605  * Parameters:
606  *        associd             int       requested associaton ID
607  *
608  * Returns:
609  *        int                 1 (one) if the peervars have been read
610  *                            - OR -
611  *                            0 (zero) if an error occured and the variable set
612  *                            could not be read
613  ****************************************************************************/
614 int
ntpq_get_assoc_peervars(associd_t associd)615 ntpq_get_assoc_peervars(
616           associd_t associd
617           )
618 {
619           peervarlen = ntpq_read_assoc_peervars(associd, peervars,
620                                                         sizeof(peervars));
621           if (peervarlen <= 0) {
622                     peervar_assoc = 0;
623 
624                     return 0;
625           }
626           peervar_assoc = associd;
627 
628           return 1;
629 }
630 
631 
632 /*****************************************************************************
633  *
634  *  ntp_read_assoc_clockvars
635  *
636  *  This function reads the clockvars variable-set of a specified association
637  *  from a NTP host and writes it to the result buffer specified, honoring
638  *  the maxsize limit.
639  *
640  *  It returns the number of bytes written or 0 when the variable-set is
641  *  empty or failed to read.
642  *
643  ****************************************************************************
644  * Parameters:
645  *        associd             int       requested associaton ID
646  *        resultbuf char*     character buffer where the variable set
647  *                                      should be stored
648  *        maxsize             int       the maximum number of bytes that can be
649  *                                      written to resultbuf
650  *
651  * Returns:
652  *        int                 number of chars that have been copied to
653  *                            resultbuf
654  *                            - OR -
655  *                            0 (zero) if an error occured
656  ****************************************************************************/
657 
658 int
ntpq_read_assoc_clockvars(associd_t associd,char * resultbuf,int maxsize)659 ntpq_read_assoc_clockvars(
660           associd_t associd,
661           char *              resultbuf,
662           int                 maxsize
663           )
664 {
665           const char *datap;
666           int res;
667           size_t dsize;
668           u_short rstatus;
669 
670           res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd,
671                                      0, &rstatus, &dsize, &datap);
672           if (res != 0)
673                     return 0;
674 
675           if (dsize == 0) {
676                     if (numhosts > 1) /* no information returned from server */
677                               return 0;
678           } else {
679                     if (dsize > maxsize)
680                               dsize = maxsize;
681                     memcpy(resultbuf, datap, dsize);
682           }
683 
684           return dsize;
685 }
686 
687 
688 
689 /*****************************************************************************
690  *
691  *  ntpq_get_assoc_clocktype
692  *
693  *  This function returns a clocktype value for a given association number
694  *  (not ID!):
695  *
696  *  NTP_CLOCKTYPE_UNKNOWN   Unknown clock type
697  *  NTP_CLOCKTYPE_BROADCAST Broadcast server
698  *  NTP_CLOCKTYPE_LOCAL     Local clock
699  *  NTP_CLOCKTYPE_UNICAST   Unicast server
700  *  NTP_CLOCKTYPE_MULTICAST Multicast server
701  *
702  ****************************************************************************/
703 int
ntpq_get_assoc_clocktype(int assoc_index)704 ntpq_get_assoc_clocktype(
705           int assoc_index
706           )
707 {
708           associd_t associd;
709           int                 i;
710           int                 rc;
711           sockaddr_u          dum_store;
712           char                dstadr[LENHOSTNAME];
713           char                resultbuf[NTPQ_BUFLEN];
714 
715           if (assoc_index < 0 || assoc_index >= numassoc)
716                     return -1;
717 
718           associd = assoc_cache[assoc_index].assid;
719           if (associd == peervar_assoc) {
720                     rc = ntpq_get_peervar("dstadr", dstadr, sizeof(dstadr));
721           } else {
722                     i = ntpq_read_assoc_peervars(associd, resultbuf,
723                                                        sizeof(resultbuf));
724                     if (i <= 0)
725                               return -1;
726                     rc = ntpq_getvar(resultbuf, i, "dstadr", dstadr,
727                                          sizeof(dstadr));
728           }
729 
730           if (0 != rc && decodenetnum(dstadr, &dum_store))
731                     return ntpq_decodeaddrtype(&dum_store);
732 
733           return -1;
734 }
735 
736 
737 
738 /*****************************************************************************
739  *
740  *  ntpq_get_assoc_clockvars
741  *
742  *  With this function the clock variables of the specified association are
743  *  requested from a NTP host. This makes only sense for associations with
744  *  the type 'l' (Local Clock) and you should check this with
745  *  ntpq_get_assoc_clocktype for each association, before you use this function
746  *  on it.
747  *
748  ****************************************************************************
749  * Parameters:
750  *        associd             int       requested associaton ID
751  *
752  * Returns:
753  *        int                 1 (one) if the clockvars have been read
754  *                            - OR -
755  *                            0 (zero) if an error occured and the variable set
756  *                            could not be read
757  ****************************************************************************/
ntpq_get_assoc_clockvars(associd_t associd)758 int  ntpq_get_assoc_clockvars( associd_t associd )
759 {
760           if (NTP_CLOCKTYPE_LOCAL != ntpq_get_assoc_clocktype(
761               ntpq_get_assoc_number(associd)))
762                     return 0;
763           clockvarlen = ntpq_read_assoc_clockvars( associd, clockvars,
764                                                              sizeof(clockvars) );
765           if ( clockvarlen <= 0 ) {
766                     clockvar_assoc = 0;
767                     return 0;
768           } else {
769                     clockvar_assoc = associd;
770                     return 1;
771           }
772 }
773 
774 
775