libntpq.c   [plain text]


/*****************************************************************************
 *
 *  libntpq.c
 *
 *  This is the wrapper library for ntpq, the NTP query utility. 
 *  This library reuses the sourcecode from ntpq and exports a number
 *  of useful functions in a library that can be linked against applications
 *  that need to query the status of a running ntpd. The whole 
 *  communcation is based on mode 6 packets.
 *
 ****************************************************************************/
#define _LIBNTPQC
#define NO_MAIN_ALLOWED 1
/* #define BUILD_AS_LIB		Already provided by the Makefile */

#include "ntpq.c"
#include "libntpq.h"

/* Function Prototypes */
int ntpq_openhost(char *);
int ntpq_closehost(void);
int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen);
int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen );
int ntpq_queryhost_peervars(unsigned short association, char *resultbuf, int maxlen);
int ntpq_getvar( char *resultbuf, int datalen, const char *varname, char *varvalue, int maxlen);
int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen);
int ntpq_read_associations ( unsigned short resultbuf[], int max_entries );
int ntpq_read_sysvars( char *resultbuf, int maxsize );
int ntpq_get_assoc_allvars( int associd  );
int ntpq_get_sysvars( void );
int ntpq_get_assocs ( void );
int ntpq_read_assoc_peervars( int associd, char *resultbuf, int maxsize );
int ntpq_read_assoc_clockvars( int associd, char *resultbuf, int maxsize );
int ntpq_get_assoc_number ( int associd );
int ntpq_get_assoc_peervars( int associd );
int ntpq_get_assoc_clockvars( int associd );
int ntpq_get_assoc_clocktype ( int assoc_number );
 

const char *Version = "libntpq 0.3beta";

/* global variables used for holding snapshots of data */
 char peervars[NTPQ_BUFLEN];
 int peervarlen = 0;
 int peervar_assoc = 0;
 char clockvars[NTPQ_BUFLEN];
 int clockvarlen = 0;
 int clockvar_assoc = 0;
 char sysvars[NTPQ_BUFLEN];
 int sysvarlen = 0;
 char *ntpq_resultbuffer[NTPQ_BUFLEN];
 unsigned short ntpq_associations[MAXASSOC];

struct ntpq_varlist ntpq_varlist[MAXLIST];

/*****************************************************************************
 *
 *  ntpq_stripquotes
 *
 *  Parses a given character buffer srcbuf and removes all quoted
 *  characters. The resulting string is copied to the specified 
 *  resultbuf character buffer.  E.g. \" will be translated into "
 * 
 ****************************************************************************
 * Parameters:
 *	resultbuf	char*	The resulting string without quoted
 *				characters
 *	srcbuf		char*	The buffer holding the original string
 *	datalen		int	The number of bytes stored in srcbuf
 *	maxlen		int	Max. number of bytes for resultbuf
 *
 * Returns:
 *	int		number of chars that have been copied to 
 *			resultbuf 
 ****************************************************************************/

int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen )
{
	char* tmpbuf = srcbuf;

	while ( *tmpbuf != 0 )
	{
		if ( *tmpbuf == '\"' )
		{
			tmpbuf++;
			continue;
		}
		
		if ( *tmpbuf == '\\' )
		{
			tmpbuf++;
			switch ( *tmpbuf )
			{
				/* ignore if end of string */
				case 0:
					continue;
				/* skip and do not copy */
				case '\"': /* quotes */
				case 'n': /*newline*/
				case 'r': /*carriage return*/
				case 'g': /*bell*/
				case 't': /*tab*/
					tmpbuf++;
					continue;
			}
		} 

		*resultbuf++ = *tmpbuf++;
		
	}
	
	*resultbuf = 0;
	return strlen(resultbuf);
}			


/*****************************************************************************
 *
 *  ntpq_getvar
 *
 *  This function parses a given buffer for a variable/value pair and
 *  copies the value of the requested variable into the specified
 *  varvalue buffer.
 *
 *  It returns the number of bytes copied or zero for an empty result
 *  (=no matching variable found or empty value)
 *
 ****************************************************************************
 * Parameters:
 *	resultbuf	char*	The resulting string without quoted
 *				characters
 *	datalen		int	The number of bytes stored in 
 *							resultbuf
 *	varname		char*	Name of the required variable 
 *	varvalue	char*	Where the value of the variable should
 *							be stored
 *	maxlen		int	Max. number of bytes for varvalue
 *
 * Returns:
 *	int		number of chars that have been copied to 
 *			varvalue
 ****************************************************************************/

int ntpq_getvar( char *resultbuf, int datalen, const char *varname, char *varvalue, int maxlen)
{
    char *name;
    char *value = NULL;

            while (nextvar(&datalen, &resultbuf, &name, &value)) {

                if ( strcmp(varname, name) == 0 ) {
			ntpq_stripquotes(varvalue,value,strlen(value),maxlen);
			return strlen(varvalue);
                }
            }

            return 0;
}


/*****************************************************************************
 *
 *  ntpq_queryhost
 *
 *  Sends a mode 6 query packet to the current open host (see 
 *  ntpq_openhost) and stores the requested variable set in the specified
 *  character buffer. 
 *  It returns the number of bytes read or zero for an empty result
 *  (=no answer or empty value)
 *
 ****************************************************************************
 * Parameters:
 *      VARSET		u_short	Which variable set should be
 *				read (PEERVARS or CLOCKVARS)
 *	association	int	The association ID that should be read
 *				0 represents the ntpd instance itself
 *	resultbuf	char*	The resulting string without quoted
 *				characters
 *	maxlen		int	Max. number of bytes for varvalue
 *
 * Returns:
 *	int		number of bytes that have been copied to 
 *			resultbuf
 *  			- OR -
 *			0 (zero) if no reply has been received or
 *			another failure occured
 ****************************************************************************/

int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen)
{
	char *datap;
	int res;
	int dsize;
	u_short rstatus;
	
	if ( numhosts > 0 )
		res = doquery(VARSET,association,0,0, (char *)0, &rstatus, &dsize, &datap);
	else
		return 0;
	
	if ( ( res != 0) || ( dsize == 0 ) ) /* no data */
		return 0;
	
	if ( dsize > maxlen) 
		dsize = maxlen;
	
	
	/* fill result resultbuf */
	memcpy(resultbuf, datap, dsize);
	
	return dsize;
}



/*****************************************************************************
 *
 *  ntpq_openhost
 *
 *  Sets up a connection to the ntpd instance of a specified host. Note:
 *  There is no real "connection" established because NTP solely works
 *  based on UDP.
 *
 ****************************************************************************
 * Parameters:
 *	hostname	char*	Hostname/IP of the host running ntpd
 *
 * Returns:
 *	int		1 if the host connection could be set up, i.e. 
 *			name resolution was succesful and/or IP address
 *			has been validated
 *  			- OR -
 *			0 (zero) if a failure occured
 ****************************************************************************/

int ntpq_openhost(char *hostname)
{
	if ( openhost(hostname) )
	{
		numhosts = 1;
	} else {
		numhosts = 0;
	}
	
	return numhosts;
	
}


/*****************************************************************************
 *
 *  ntpq_closehost
 *
 *  Cleans up a connection by closing the used socket. Should be called
 *  when no further queries are required for the currently used host.
 *
 ****************************************************************************
 * Parameters:
 *	- none -
 *
 * Returns:
 *	int		0 (zero) if no host has been opened before
 *			- OR -
 *			the resultcode from the closesocket function call
 ****************************************************************************/

int ntpq_closehost(void)
{
	if ( numhosts )
	 return closesocket(sockfd);
	
	return 0;
}


/*****************************************************************************
 *
 *  ntpq_read_associations
 *
 *  This function queries the ntp host for its associations and returns the 
 *  number of associations found.
 *
 *  It takes an u_short array as its first parameter, this array holds the 
 *  IDs of the associations, 
 *  the function will not write more entries than specified with the 
 *  max_entries parameter.
 *
 *  However, if more than max_entries associations were found, the return 
 *  value of this function will reflect the real number, even if not all 
 *  associations have been stored in the array.
 *
 ****************************************************************************
 * Parameters:
 *	resultbuf	u_short*Array that should hold the list of
 *				association IDs
 *	maxentries	int	maximum number of association IDs that can
 *				be stored in resultbuf
 *
 * Returns:
 *	int		number of association IDs stored in resultbuf
 *  			- OR -
 *			0 (zero) if a failure occured or no association has
 *			been returned.
 ****************************************************************************/
 
 int  ntpq_read_associations ( u_short resultbuf[], int max_entries )
{
    int i = 0;

    if (ntpq_dogetassoc()) {       
        
        if(numassoc < max_entries)
          max_entries = numassoc;

        for (i=0;i<max_entries;i++)
            resultbuf[i] = assoc_cache[i].assid;

        return numassoc;
    }

    return 0;
}




/*****************************************************************************
 *
 *  ntpq_get_assocs
 *
 *  This function reads the associations of a previously selected (with 
 *  ntpq_openhost) NTP host into its own (global) array and returns the 
 *  number of associations found. 
 *
 *  The obtained association IDs can be read by using the ntpq_get_assoc_id 
 *  function.
 *
 ****************************************************************************
 * Parameters:
 *	- none -
 *
 * Returns:
 *	int		number of association IDs stored in resultbuf
 *  			- OR -
 *			0 (zero) if a failure occured or no association has
 *			been returned.
 ****************************************************************************/
 
 int  ntpq_get_assocs ( void )
{
    return ntpq_read_associations( ntpq_associations, MAXASSOC );
}


/*****************************************************************************
 *  
 *  ntpq_get_assoc_number
 *
 *  This function returns for a given Association ID the association number 
 *  in the internal association array, which is filled by the ntpq_get_assocs 
 *  function.
 * 
 ****************************************************************************
 * Parameters:
 *	associd		int	requested associaton ID 
 *
 * Returns:
 *	int		the number of the association array element that is
 *			representing the given association ID
 *  			- OR -
 *			-1 if a failure occured or no matching association 
 * 			ID has been found
 ****************************************************************************/
 
int ntpq_get_assoc_number ( int associd )
{
   int i = 0;

   for (i=0;i<numassoc;i++) {
     if (assoc_cache[i].assid == associd)
       return i;
    }

   return (-1);

}


/*****************************************************************************
 *  
 *  ntpq_read_assoc_peervars
 *
 *  This function reads the peervars variable-set of a specified association 
 *  from a NTP host and writes it to the result buffer specified, honoring 
 *  the maxsize limit.
 *
 *  It returns the number of bytes written or 0 when the variable-set is 
 *  empty or failed to read.
 *  
 ****************************************************************************
 * Parameters:
 *	associd		int	requested associaton ID 
 *	resultbuf	char*	character buffer where the variable set
 *				should be stored
 *	maxsize		int	the maximum number of bytes that can be
 *				written to resultbuf
 *
 * Returns:
 *	int		number of chars that have been copied to 
 *			resultbuf
 *			- OR - 
 *			0 (zero) if an error occured
 ****************************************************************************/

  int ntpq_read_assoc_peervars( int associd, char *resultbuf, int maxsize )
{

    char *datap;
    int res;
    int dsize;
    u_short rstatus;
   	l_fp rec;
	  l_fp ts;
    char value[NTPQ_BUFLEN];


    res = doquery(CTL_OP_READVAR, associd, 0, 0, (char *)0, &rstatus,
              &dsize, &datap);
    
    if (res != 0)
        return 0;

   	get_systime(&ts);

    if (dsize == 0) {
        if (numhosts > 1)
            (void) fprintf(stderr, "server=%s ", currenthost);
        (void) fprintf(stderr,
                   "***No information returned for association %d\n",
                   associd);
        return 0;
    } else {
        if ( dsize > maxsize ) 
            dsize = maxsize;

        memcpy(resultbuf,datap,dsize);        
        resultbuf[dsize]=0x0;
        
        ntpq_getvar(resultbuf, dsize, "rec", value, sizeof (value) );

        if (!decodets(value, &rec))
				  L_CLR(&rec);

        memcpy(resultbuf,value,maxsize);        
        resultbuf[dsize]=0x0;
        dsize=strlen(resultbuf);
        

    }
    return dsize;

}




/*****************************************************************************
 *  
 *  ntpq_read_sysvars
 *
 *  This function reads the sysvars variable-set from a NTP host and writes it
 *  to the result buffer specified, honoring the maxsize limit.
 *
 *  It returns the number of bytes written or 0 when the variable-set is empty
 *  or could not be read.
 *  
 ****************************************************************************
 * Parameters:
 *	resultbuf	char*	character buffer where the variable set
 *				should be stored
 *	maxsize		int	the maximum number of bytes that can be
 *				written to resultbuf
 *
 * Returns:
 *	int		number of chars that have been copied to 
 *			resultbuf
 *			- OR - 
 *			0 (zero) if an error occured
 ****************************************************************************/
int ntpq_read_sysvars( char *resultbuf, int maxsize )
{

    char *datap;
    int res;
    int dsize;
    u_short rstatus;

    res = doquery(CTL_OP_READVAR, 0, 0, 0, (char *)0, &rstatus,
              &dsize, &datap);
    
    if (res != 0)
        return 0;

    if (dsize == 0) {
        if (numhosts > 1)
            (void) fprintf(stderr, "server=%s ", currenthost);
        (void) fprintf(stderr,
                   "***No sysvar information returned \n");
        return 0;
    } else {
        if ( dsize > maxsize ) 
            dsize = maxsize;

        memcpy(resultbuf,datap,dsize);
    }

    return dsize;

}


/*****************************************************************************
 *  ntpq_get_assoc_allvars
 *
 *  With this function all association variables for the specified association
 *  ID can be requested from a NTP host. They are stored internally and can be
 *  read by using the ntpq_get_peervar or ntpq_get_clockvar functions.
 *
 *  Basically this is only a combination of the ntpq_get_assoc_peervars and 
 *  ntpq_get_assoc_clockvars functions.
 *
 *  It returns 1 if both variable-sets (peervars and clockvars) were 
 *  received successfully. If one variable-set or both of them weren't 
 *  received,
 *
 ****************************************************************************
 * Parameters:
 *	associd		int	requested associaton ID 
 *
 * Returns:
 *	int		nonzero if at least one variable set could be read
 * 			- OR - 
 *			0 (zero) if an error occured and both variable sets
 *			could not be read
 ****************************************************************************/
 int  ntpq_get_assoc_allvars( int associd  )
{
    return ( ntpq_get_assoc_peervars ( associd ) & ntpq_get_assoc_clockvars( associd ) );
}




/*****************************************************************************
 *
 *  ntpq_get_sysvars
 *
 *  The system variables of a NTP host can be requested by using this function
 *  and afterwards using ntpq_get_sysvar to read the single variable values.
 *
 ****************************************************************************
 * Parameters:
 *	- none -
 *
 * Returns:
 *	int		nonzero if the variable set could be read
 * 			- OR - 
 *			0 (zero) if an error occured and the sysvars
 *			could not be read
 ****************************************************************************/
 int  ntpq_get_sysvars( void )
{
        sysvarlen = ( ntpq_read_sysvars( sysvars, sizeof(sysvars )) );
        if ( sysvarlen <= 0 ) {
            return 0;
        } else {
            return 1;
        }
}


/*****************************************************************************
 *  
 *  ntp_get_peervar
 *
 *  This function uses the variable-set which was read by using 
 *  ntp_get_peervars and searches for a variable specified with varname. If 
 *  such a variable exists, it writes its value into
 *  varvalue (maxlen specifies the size of this target buffer).
 *  
 ****************************************************************************
 * Parameters:
 *	varname		char*	requested variable name
 *	varvalue	char*	the buffer where the value should go into
 *	maxlen		int	maximum number of bytes that can be copied to
 *				varvalue
 *
 * Returns:
 *	int		number of bytes copied to varvalue
 * 			- OR - 
 *			0 (zero) if an error occured or the variable could 
 *			not be found
 ****************************************************************************/
int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen)
{
    return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) );
}



/*****************************************************************************
 *  
 *  ntpq_get_assoc_peervars
 *
 *  This function requests the peer variables of the specified association 
 *  from a NTP host. In order to access the variable values, the function 
 *  ntpq_get_peervar must be used.
 *
 ****************************************************************************
 * Parameters:
 *	associd		int	requested associaton ID 
 *
 * Returns:
 *	int		1 (one) if the peervars have been read
 * 			- OR - 
 *			0 (zero) if an error occured and the variable set
 *			could not be read
 ****************************************************************************/
 int  ntpq_get_assoc_peervars( int associd )
{
        peervarlen = ( ntpq_read_assoc_peervars( associd, peervars, sizeof(peervars )) );
        if ( peervarlen <= 0 ) {
            peervar_assoc = 0;
            return 0;
        } else {
            peervar_assoc = associd;
            return 1;
        }
}


/*****************************************************************************
 *  
 *  ntp_read_assoc_clockvars
 *
 *  This function reads the clockvars variable-set of a specified association
 *  from a NTP host and writes it to the result buffer specified, honoring 
 *  the maxsize limit.
 *
 *  It returns the number of bytes written or 0 when the variable-set is 
 *  empty or failed to read.
 *  
 ****************************************************************************
 * Parameters:
 *	associd		int	requested associaton ID 
 *	resultbuf	char*	character buffer where the variable set
 *				should be stored
 *	maxsize		int	the maximum number of bytes that can be
 *				written to resultbuf
 *
 * Returns:
 *	int		number of chars that have been copied to 
 *			resultbuf
 *			- OR - 
 *			0 (zero) if an error occured
 ****************************************************************************/

int ntpq_read_assoc_clockvars( int associd, char *resultbuf, int maxsize )
{

    char *datap;
    int res;
    int dsize;
    u_short rstatus;
    
    res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd, 0, &rstatus, &dsize, &datap);
    
    if (res != 0)
        return 0;

    if (dsize == 0) {
        if (numhosts > 1) /* no information returned from server */
	return 0;
    } else {
        if ( dsize > maxsize ) 
            dsize = maxsize;

        memcpy(resultbuf,datap,dsize);
    }

    return dsize;
}



/*****************************************************************************
 *  
 *  ntpq_get_assoc_clocktype
 *
 *  This function returns a clocktype value for a given association number 
 *  (not ID!):
 *
 *  NTP_CLOCKTYPE_UNKNOWN   Unknown clock type
 *  NTP_CLOCKTYPE_BROADCAST Broadcast server
 *  NTP_CLOCKTYPE_LOCAL     Local clock
 *  NTP_CLOCKTYPE_UNICAST   Unicast server
 *  NTP_CLOCKTYPE_MULTICAST Multicast server
 * 
 ****************************************************************************/
 int ntpq_get_assoc_clocktype ( int assoc_number )
{
    int type = 0;
    int i, rc = 0;
    sockaddr_u dum_store;
    char value[LENHOSTNAME];
    char resultbuf[1024];


    if ( assoc_number < 0 || assoc_number > numassoc ) {
        return -1;
    } else {
        if ( peervar_assoc != assoc_cache[assoc_number].assid ) {

            i=ntpq_read_assoc_peervars(assoc_cache[assoc_number].assid, resultbuf, sizeof(resultbuf));
            if ( i <= 0 ) {
                return -1;
            }

            rc = ntpq_getvar(resultbuf, i, "dstadr", value, LENHOSTNAME );


        } else {
            
            rc = ntpq_get_peervar("dstadr",value,LENHOSTNAME);

        }

        if ( rc ) {
            if (decodenetnum(value, &dum_store)) {
		type = ntpq_decodeaddrtype(&dum_store);
                return type;
            }
        }

        return -1;
    }

    return -1;

}



/*****************************************************************************
 *  
 *  ntpq_get_assoc_clockvars
 *
 *  With this function the clock variables of the specified association are 
 *  requested from a NTP host. This makes only sense for associations with 
 *  the type 'l' (Local Clock) and you should check this with 
 *  ntpq_get_assoc_clocktype for each association, before you use this function
 *  on it.
 *
 ****************************************************************************
 * Parameters:
 *	associd		int	requested associaton ID 
 *
 * Returns:
 *	int		1 (one) if the clockvars have been read
 * 			- OR - 
 *			0 (zero) if an error occured and the variable set
 *			could not be read
 ****************************************************************************/
 int  ntpq_get_assoc_clockvars( int associd )
{
        
        if ( ntpq_get_assoc_clocktype(ntpq_get_assoc_number(associd)) != NTP_CLOCKTYPE_LOCAL )
            return 0;
  
        clockvarlen = ( ntpq_read_assoc_clockvars( associd, clockvars, sizeof(clockvars )) );
        if ( clockvarlen <= 0 ) {
            clockvar_assoc = 0;
            return 0;
        } else {
            clockvar_assoc = associd;
            return 1;
        }
}