xref: /freebsd/contrib/ntp/ntpq/libntpq.c (revision 1f4bcc459a76b7aa664f3fd557684cd0ba6da352)
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 	size_t	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 	size_t	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 	size_t		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 	size_t		dsize;
478 	u_short		rstatus;
479 
480 	res = doquery(CTL_OP_READVAR, 0, 0, 0, NULL, &rstatus,
481 		      &dsize, &datap);
482 
483 	if (res != 0)
484 		return 0;
485 
486 	if (dsize == 0) {
487 		if (numhosts > 1)
488 			fprintf(stderr, "server=%s ", currenthost);
489 		fprintf(stderr, "***No sysvar information returned\n");
490 
491 		return 0;
492 	} else {
493 		dsize = min(dsize, maxsize);
494 		memcpy(resultbuf, datap, dsize);
495 	}
496 
497 	return dsize;
498 }
499 
500 
501 /*****************************************************************************
502  *  ntpq_get_assoc_allvars
503  *
504  *  With this function all association variables for the specified association
505  *  ID can be requested from a NTP host. They are stored internally and can be
506  *  read by using the ntpq_get_peervar or ntpq_get_clockvar functions.
507  *
508  *  Basically this is only a combination of the ntpq_get_assoc_peervars and
509  *  ntpq_get_assoc_clockvars functions.
510  *
511  *  It returns 1 if both variable-sets (peervars and clockvars) were
512  *  received successfully. If one variable-set or both of them weren't
513  *  received,
514  *
515  ****************************************************************************
516  * Parameters:
517  *	associd		int	requested associaton ID
518  *
519  * Returns:
520  *	int		nonzero if at least one variable set could be read
521  * 			- OR -
522  *			0 (zero) if an error occured and both variable sets
523  *			could not be read
524  ****************************************************************************/
525  int  ntpq_get_assoc_allvars( associd_t associd  )
526 {
527 	return ntpq_get_assoc_peervars ( associd ) &
528 	       ntpq_get_assoc_clockvars( associd );
529 }
530 
531 
532 
533 
534 /*****************************************************************************
535  *
536  *  ntpq_get_sysvars
537  *
538  *  The system variables of a NTP host can be requested by using this function
539  *  and afterwards using ntpq_get_sysvar to read the single variable values.
540  *
541  ****************************************************************************
542  * Parameters:
543  *	- none -
544  *
545  * Returns:
546  *	int		nonzero if the variable set could be read
547  * 			- OR -
548  *			0 (zero) if an error occured and the sysvars
549  *			could not be read
550  ****************************************************************************/
551 int
552 ntpq_get_sysvars(void)
553 {
554 	sysvarlen = ntpq_read_sysvars(sysvars, sizeof(sysvars));
555 	if (sysvarlen <= 0)
556 		return 0;
557 	else
558 		return 1;
559 }
560 
561 
562 /*****************************************************************************
563  *
564  *  ntp_get_peervar
565  *
566  *  This function uses the variable-set which was read by using
567  *  ntp_get_peervars and searches for a variable specified with varname. If
568  *  such a variable exists, it writes its value into
569  *  varvalue (maxlen specifies the size of this target buffer).
570  *
571  ****************************************************************************
572  * Parameters:
573  *	varname		char*	requested variable name
574  *	varvalue	char*	the buffer where the value should go into
575  *	maxlen		int	maximum number of bytes that can be copied to
576  *				varvalue
577  *
578  * Returns:
579  *	int		number of bytes copied to varvalue
580  * 			- OR -
581  *			0 (zero) if an error occured or the variable could
582  *			not be found
583  ****************************************************************************/
584 int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen)
585 {
586     return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) );
587 }
588 
589 
590 
591 /*****************************************************************************
592  *
593  *  ntpq_get_assoc_peervars
594  *
595  *  This function requests the peer variables of the specified association
596  *  from a NTP host. In order to access the variable values, the function
597  *  ntpq_get_peervar must be used.
598  *
599  ****************************************************************************
600  * Parameters:
601  *	associd		int	requested associaton ID
602  *
603  * Returns:
604  *	int		1 (one) if the peervars have been read
605  * 			- OR -
606  *			0 (zero) if an error occured and the variable set
607  *			could not be read
608  ****************************************************************************/
609 int
610 ntpq_get_assoc_peervars(
611 	associd_t associd
612 	)
613 {
614 	peervarlen = ntpq_read_assoc_peervars(associd, peervars,
615 					      sizeof(peervars));
616 	if (peervarlen <= 0) {
617 		peervar_assoc = 0;
618 
619 		return 0;
620 	}
621 	peervar_assoc = associd;
622 
623 	return 1;
624 }
625 
626 
627 /*****************************************************************************
628  *
629  *  ntp_read_assoc_clockvars
630  *
631  *  This function reads the clockvars variable-set of a specified association
632  *  from a NTP host and writes it to the result buffer specified, honoring
633  *  the maxsize limit.
634  *
635  *  It returns the number of bytes written or 0 when the variable-set is
636  *  empty or failed to read.
637  *
638  ****************************************************************************
639  * Parameters:
640  *	associd		int	requested associaton ID
641  *	resultbuf	char*	character buffer where the variable set
642  *				should be stored
643  *	maxsize		int	the maximum number of bytes that can be
644  *				written to resultbuf
645  *
646  * Returns:
647  *	int		number of chars that have been copied to
648  *			resultbuf
649  *			- OR -
650  *			0 (zero) if an error occured
651  ****************************************************************************/
652 
653 int
654 ntpq_read_assoc_clockvars(
655 	associd_t	associd,
656 	char *		resultbuf,
657 	int		maxsize
658 	)
659 {
660 	const char *datap;
661 	int res;
662 	size_t dsize;
663 	u_short rstatus;
664 
665 	res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd,
666 			       0, &rstatus, &dsize, &datap);
667 	if (res != 0)
668 		return 0;
669 
670 	if (dsize == 0) {
671 		if (numhosts > 1) /* no information returned from server */
672 			return 0;
673 	} else {
674 		if (dsize > maxsize)
675 			dsize = maxsize;
676 		memcpy(resultbuf, datap, dsize);
677 	}
678 
679 	return dsize;
680 }
681 
682 
683 
684 /*****************************************************************************
685  *
686  *  ntpq_get_assoc_clocktype
687  *
688  *  This function returns a clocktype value for a given association number
689  *  (not ID!):
690  *
691  *  NTP_CLOCKTYPE_UNKNOWN   Unknown clock type
692  *  NTP_CLOCKTYPE_BROADCAST Broadcast server
693  *  NTP_CLOCKTYPE_LOCAL     Local clock
694  *  NTP_CLOCKTYPE_UNICAST   Unicast server
695  *  NTP_CLOCKTYPE_MULTICAST Multicast server
696  *
697  ****************************************************************************/
698 int
699 ntpq_get_assoc_clocktype(
700 	int assoc_index
701 	)
702 {
703 	associd_t	associd;
704 	int		i;
705 	int		rc;
706 	sockaddr_u	dum_store;
707 	char		dstadr[LENHOSTNAME];
708 	char		resultbuf[NTPQ_BUFLEN];
709 
710 	if (assoc_index < 0 || assoc_index >= numassoc)
711 		return -1;
712 
713 	associd = assoc_cache[assoc_index].assid;
714 	if (associd == peervar_assoc) {
715 		rc = ntpq_get_peervar("dstadr", dstadr, sizeof(dstadr));
716 	} else {
717 		i = ntpq_read_assoc_peervars(associd, resultbuf,
718 					     sizeof(resultbuf));
719 		if (i <= 0)
720 			return -1;
721 		rc = ntpq_getvar(resultbuf, i, "dstadr", dstadr,
722 				 sizeof(dstadr));
723 	}
724 
725 	if (0 != rc && decodenetnum(dstadr, &dum_store))
726 		return ntpq_decodeaddrtype(&dum_store);
727 
728 	return -1;
729 }
730 
731 
732 
733 /*****************************************************************************
734  *
735  *  ntpq_get_assoc_clockvars
736  *
737  *  With this function the clock variables of the specified association are
738  *  requested from a NTP host. This makes only sense for associations with
739  *  the type 'l' (Local Clock) and you should check this with
740  *  ntpq_get_assoc_clocktype for each association, before you use this function
741  *  on it.
742  *
743  ****************************************************************************
744  * Parameters:
745  *	associd		int	requested associaton ID
746  *
747  * Returns:
748  *	int		1 (one) if the clockvars have been read
749  * 			- OR -
750  *			0 (zero) if an error occured and the variable set
751  *			could not be read
752  ****************************************************************************/
753 int  ntpq_get_assoc_clockvars( associd_t associd )
754 {
755 	if (NTP_CLOCKTYPE_LOCAL != ntpq_get_assoc_clocktype(
756 	    ntpq_get_assoc_number(associd)))
757 		return 0;
758 	clockvarlen = ntpq_read_assoc_clockvars( associd, clockvars,
759 						 sizeof(clockvars) );
760 	if ( clockvarlen <= 0 ) {
761 		clockvar_assoc = 0;
762 		return 0;
763 	} else {
764 		clockvar_assoc = associd;
765 		return 1;
766 	}
767 }
768 
769 
770