xref: /freebsd/contrib/ntp/ntpq/libntpq.c (revision f0574f5cf69e168cc4ea71ebbe5fdec9ec9a3dfe)
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 
ntpq_stripquotes(char * resultbuf,char * srcbuf,int datalen,int maxlen)58 int 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 
128 size_t
ntpq_getvar(const char * resultbuf,size_t datalen,const char * varname,char * varvalue,size_t maxlen)129 ntpq_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 
ntpq_queryhost(unsigned short VARSET,unsigned short association,char * resultbuf,int maxlen)183 int 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 
231 int
ntpq_openhost(char * hostname,int fam)232 ntpq_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 
ntpq_closehost(void)266 int 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 
ntpq_read_associations(u_short resultbuf[],int max_entries)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 
ntpq_get_assocs(void)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 
ntpq_get_assoc_number(associd_t associd)374 int 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 
414 int
ntpq_read_assoc_peervars(associd_t associd,char * resultbuf,int maxsize)415 ntpq_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  ****************************************************************************/
472 size_t
ntpq_read_sysvars(char * resultbuf,size_t maxsize)473 ntpq_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  ****************************************************************************/
ntpq_get_assoc_allvars(associd_t associd)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  ****************************************************************************/
554 int
ntpq_get_sysvars(void)555 ntpq_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  ****************************************************************************/
ntpq_get_peervar(const char * varname,char * varvalue,int maxlen)587 int 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  ****************************************************************************/
612 int
ntpq_get_assoc_peervars(associd_t associd)613 ntpq_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 
656 int
ntpq_read_assoc_clockvars(associd_t associd,char * resultbuf,int maxsize)657 ntpq_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  ****************************************************************************/
701 int
ntpq_get_assoc_clocktype(int assoc_index)702 ntpq_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  ****************************************************************************/
ntpq_get_assoc_clockvars(associd_t associd)756 int  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