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