libntpq.c revision 290001
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
22const char *Version = "libntpq 0.3beta";
23
24/* global variables used for holding snapshots of data */
25char peervars[NTPQ_BUFLEN];
26int peervarlen = 0;
27associd_t peervar_assoc = 0;
28char clockvars[NTPQ_BUFLEN];
29int clockvarlen = 0;
30int clockvar_assoc = 0;
31char sysvars[NTPQ_BUFLEN];
32int sysvarlen = 0;
33char *ntpq_resultbuffer[NTPQ_BUFLEN];
34unsigned short ntpq_associations[MAXASSOC];
35struct 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
58int 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
125size_t
126ntpq_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
180int 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
228int
229ntpq_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
263int 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
371int 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
411int
412ntpq_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 ****************************************************************************/
469size_t
470ntpq_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 ****************************************************************************/
553int
554ntpq_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 ****************************************************************************/
586int 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 ****************************************************************************/
611int
612ntpq_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
655int
656ntpq_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 ****************************************************************************/
700int
701ntpq_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 ****************************************************************************/
755int  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