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