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