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