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