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