libntpq.c revision 316068
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* 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
128size_t
129ntpq_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
183int 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
231int
232ntpq_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
266int 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
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
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
374int 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
414int
415ntpq_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 ****************************************************************************/
472size_t
473ntpq_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 ****************************************************************************/
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 ****************************************************************************/
554int
555ntpq_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 ****************************************************************************/
587int 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 ****************************************************************************/
612int
613ntpq_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
656int
657ntpq_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 ****************************************************************************/
701int
702ntpq_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 ****************************************************************************/
756int  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