/***************************************************************************** * * 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 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; } }