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