net-snmp  5.4.1
lcd_time.c
00001 /*
00002  * lcd_time.c
00003  *
00004  * XXX  Should etimelist entries with <0,0> time tuples be timed out?
00005  * XXX  Need a routine to free the memory?  (Perhaps at shutdown?)
00006  */
00007 
00008 #include <net-snmp/net-snmp-config.h>
00009 
00010 #include <sys/types.h>
00011 #if HAVE_WINSOCK_H
00012 #include <winsock.h>
00013 #endif
00014 #include <stdio.h>
00015 #ifdef HAVE_STDLIB_H
00016 #include <stdlib.h>
00017 #endif
00018 #if HAVE_STRING_H
00019 #include <string.h>
00020 #else
00021 #include <strings.h>
00022 #endif
00023 #if TIME_WITH_SYS_TIME
00024 # ifdef WIN32
00025 #  include <sys/timeb.h>
00026 # else
00027 #  include <sys/time.h>
00028 # endif
00029 # include <time.h>
00030 #else
00031 # if HAVE_SYS_TIME_H
00032 #  include <sys/time.h>
00033 # else
00034 #  include <time.h>
00035 # endif
00036 #endif
00037 #ifdef HAVE_NETINET_IN_H
00038 #include <netinet/in.h>
00039 #endif
00040 
00041 #if HAVE_DMALLOC_H
00042 #include <dmalloc.h>
00043 #endif
00044 
00045 #include <net-snmp/types.h>
00046 #include <net-snmp/output_api.h>
00047 #include <net-snmp/utilities.h>
00048 
00049 #include <net-snmp/library/snmp_api.h>
00050 #include <net-snmp/library/callback.h>
00051 #include <net-snmp/library/snmp_secmod.h>
00052 #include <net-snmp/library/snmpusm.h>
00053 #include <net-snmp/library/lcd_time.h>
00054 #include <net-snmp/library/scapi.h>
00055 #include <net-snmp/library/snmpv3.h>
00056 
00057 #include <net-snmp/library/transform_oids.h>
00058 
00059 /*
00060  * Global static hashlist to contain Enginetime entries.
00061  *
00062  * New records are prepended to the appropriate list at the hash index.
00063  */
00064 static Enginetime etimelist[ETIMELIST_SIZE];
00065 
00066 
00067 
00068 
00069 /*******************************************************************-o-******
00070  * get_enginetime
00071  *
00072  * Parameters:
00073  *      *engineID
00074  *       engineID_len
00075  *      *engineboot
00076  *      *engine_time
00077  *      
00078  * Returns:
00079  *      SNMPERR_SUCCESS         Success -- when a record for engineID is found.
00080  *      SNMPERR_GENERR          Otherwise.
00081  *
00082  *
00083  * Lookup engineID and return the recorded values for the
00084  * <engine_time, engineboot> tuple adjusted to reflect the estimated time
00085  * at the engine in question.
00086  *
00087  * Special case: if engineID is NULL or if engineID_len is 0 then
00088  * the time tuple is returned immediately as zero.
00089  *
00090  * XXX  What if timediff wraps?  >shrug<
00091  * XXX  Then: you need to increment the boots value.  Now.  Detecting
00092  *            this is another matter.
00093  */
00094 int
00095 get_enginetime(u_char * engineID,
00096                u_int engineID_len,
00097                u_int * engineboot,
00098                u_int * engine_time, u_int authenticated)
00099 {
00100     int             rval = SNMPERR_SUCCESS;
00101     time_t          timediff = 0;
00102     Enginetime      e = NULL;
00103 
00104 
00105 
00106     /*
00107      * Sanity check.
00108      */
00109     if (!engine_time || !engineboot) {
00110         QUITFUN(SNMPERR_GENERR, get_enginetime_quit);
00111     }
00112 
00113 
00114     /*
00115      * Compute estimated current engine_time tuple at engineID if
00116      * a record is cached for it.
00117      */
00118     *engine_time = *engineboot = 0;
00119 
00120     if (!engineID || (engineID_len <= 0)) {
00121         QUITFUN(SNMPERR_GENERR, get_enginetime_quit);
00122     }
00123 
00124     if (!(e = search_enginetime_list(engineID, engineID_len))) {
00125         QUITFUN(SNMPERR_GENERR, get_enginetime_quit);
00126     }
00127 #ifdef LCD_TIME_SYNC_OPT
00128     if (!authenticated || e->authenticatedFlag) {
00129 #endif
00130         *engine_time = e->engineTime;
00131         *engineboot = e->engineBoot;
00132 
00133        timediff = snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime;
00134 
00135 #ifdef LCD_TIME_SYNC_OPT
00136     }
00137 #endif
00138 
00139     if (timediff > (int) (ENGINETIME_MAX - *engine_time)) {
00140         *engine_time = (timediff - (ENGINETIME_MAX - *engine_time));
00141 
00142         /*
00143          * FIX -- move this check up... should not change anything
00144          * * if engineboot is already locked.  ???
00145          */
00146         if (*engineboot < ENGINEBOOT_MAX) {
00147             *engineboot += 1;
00148         }
00149 
00150     } else {
00151         *engine_time += timediff;
00152     }
00153 
00154     DEBUGMSGTL(("lcd_get_enginetime", "engineID "));
00155     DEBUGMSGHEX(("lcd_get_enginetime", engineID, engineID_len));
00156     DEBUGMSG(("lcd_get_enginetime", ": boots=%d, time=%d\n", *engineboot,
00157               *engine_time));
00158 
00159   get_enginetime_quit:
00160     return rval;
00161 
00162 }                               /* end get_enginetime() */
00163 
00164 /*******************************************************************-o-******
00165  * get_enginetime
00166  *
00167  * Parameters:
00168  *      *engineID
00169  *       engineID_len
00170  *      *engineboot
00171  *      *engine_time
00172  *      
00173  * Returns:
00174  *      SNMPERR_SUCCESS         Success -- when a record for engineID is found.
00175  *      SNMPERR_GENERR          Otherwise.
00176  *
00177  *
00178  * Lookup engineID and return the recorded values for the
00179  * <engine_time, engineboot> tuple adjusted to reflect the estimated time
00180  * at the engine in question.
00181  *
00182  * Special case: if engineID is NULL or if engineID_len is 0 then
00183  * the time tuple is returned immediately as zero.
00184  *
00185  * XXX  What if timediff wraps?  >shrug<
00186  * XXX  Then: you need to increment the boots value.  Now.  Detecting
00187  *            this is another matter.
00188  */
00189 int
00190 get_enginetime_ex(u_char * engineID,
00191                   u_int engineID_len,
00192                   u_int * engineboot,
00193                   u_int * engine_time,
00194                   u_int * last_engine_time, u_int authenticated)
00195 {
00196     int             rval = SNMPERR_SUCCESS;
00197     time_t          timediff = 0;
00198     Enginetime      e = NULL;
00199 
00200 
00201 
00202     /*
00203      * Sanity check.
00204      */
00205     if (!engine_time || !engineboot || !last_engine_time) {
00206         QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit);
00207     }
00208 
00209 
00210     /*
00211      * Compute estimated current engine_time tuple at engineID if
00212      * a record is cached for it.
00213      */
00214     *last_engine_time = *engine_time = *engineboot = 0;
00215 
00216     if (!engineID || (engineID_len <= 0)) {
00217         QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit);
00218     }
00219 
00220     if (!(e = search_enginetime_list(engineID, engineID_len))) {
00221         QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit);
00222     }
00223 #ifdef LCD_TIME_SYNC_OPT
00224     if (!authenticated || e->authenticatedFlag) {
00225 #endif
00226         *last_engine_time = *engine_time = e->engineTime;
00227         *engineboot = e->engineBoot;
00228 
00229        timediff = snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime;
00230 
00231 #ifdef LCD_TIME_SYNC_OPT
00232     }
00233 #endif
00234 
00235     if (timediff > (int) (ENGINETIME_MAX - *engine_time)) {
00236         *engine_time = (timediff - (ENGINETIME_MAX - *engine_time));
00237 
00238         /*
00239          * FIX -- move this check up... should not change anything
00240          * * if engineboot is already locked.  ???
00241          */
00242         if (*engineboot < ENGINEBOOT_MAX) {
00243             *engineboot += 1;
00244         }
00245 
00246     } else {
00247         *engine_time += timediff;
00248     }
00249 
00250     DEBUGMSGTL(("lcd_get_enginetime_ex", "engineID "));
00251     DEBUGMSGHEX(("lcd_get_enginetime_ex", engineID, engineID_len));
00252     DEBUGMSG(("lcd_get_enginetime_ex", ": boots=%d, time=%d\n",
00253               *engineboot, *engine_time));
00254 
00255   get_enginetime_ex_quit:
00256     return rval;
00257 
00258 }                               /* end get_enginetime_ex() */
00259 
00260 
00261 void free_enginetime(unsigned char *engineID, size_t engineID_len)
00262 {
00263     Enginetime      e = NULL;
00264     int             rval = 0;
00265 
00266     rval = hash_engineID(engineID, engineID_len);
00267     if (rval < 0)
00268         return;
00269 
00270     e = etimelist[rval];
00271 
00272     while (e != NULL) {
00273         etimelist[rval] = e->next;
00274         SNMP_FREE(e->engineID);
00275         SNMP_FREE(e);
00276         e = etimelist[rval];
00277     }
00278 
00279 }
00280 
00281 /*******************************************************************-o-****
00282 **
00283  * free_etimelist
00284  *
00285  * Parameters:
00286  *   None
00287  *      
00288  * Returns:
00289  *   void
00290  *
00291  *
00292  * Free all of the memory used by entries in the etimelist.
00293  *
00294  */
00295 void free_etimelist(void)
00296 {
00297      int index = 0;
00298      Enginetime e = 0;
00299      Enginetime nextE = 0;
00300 
00301      for( ; index < ETIMELIST_SIZE; ++index)
00302      {
00303            e = etimelist[index];
00304 
00305            while(e != 0)
00306            {
00307                  nextE = e->next;
00308                  SNMP_FREE(e->engineID);
00309                  SNMP_FREE(e);
00310                  e = nextE;
00311            }
00312 
00313            etimelist[index] = 0;
00314      }
00315      return;
00316 }
00317 
00318 /*******************************************************************-o-******
00319  * set_enginetime
00320  *
00321  * Parameters:
00322  *      *engineID
00323  *       engineID_len
00324  *       engineboot
00325  *       engine_time
00326  *      
00327  * Returns:
00328  *      SNMPERR_SUCCESS         Success.
00329  *      SNMPERR_GENERR          Otherwise.
00330  *
00331  *
00332  * Lookup engineID and store the given <engine_time, engineboot> tuple
00333  * and then stamp the record with a consistent source of local time.
00334  * If the engineID record does not exist, create one.
00335  *
00336  * Special case: engineID is NULL or engineID_len is 0 defines an engineID
00337  * that is "always set."
00338  *
00339  * XXX  "Current time within the local engine" == time(NULL)...
00340  */
00341 int
00342 set_enginetime(u_char * engineID,
00343                u_int engineID_len,
00344                u_int engineboot, u_int engine_time, u_int authenticated)
00345 {
00346     int             rval = SNMPERR_SUCCESS, iindex;
00347     Enginetime      e = NULL;
00348 
00349 
00350 
00351     /*
00352      * Sanity check.
00353      */
00354     if (!engineID || (engineID_len <= 0)) {
00355         return rval;
00356     }
00357 
00358 
00359     /*
00360      * Store the given <engine_time, engineboot> tuple in the record
00361      * for engineID.  Create a new record if necessary.
00362      */
00363     if (!(e = search_enginetime_list(engineID, engineID_len))) {
00364         if ((iindex = hash_engineID(engineID, engineID_len)) < 0) {
00365             QUITFUN(SNMPERR_GENERR, set_enginetime_quit);
00366         }
00367 
00368         e = (Enginetime) calloc(1, sizeof(*e));
00369 
00370         e->next = etimelist[iindex];
00371         etimelist[iindex] = e;
00372 
00373         e->engineID = (u_char *) calloc(1, engineID_len);
00374         memcpy(e->engineID, engineID, engineID_len);
00375 
00376         e->engineID_len = engineID_len;
00377     }
00378 #ifdef LCD_TIME_SYNC_OPT
00379     if (authenticated || !e->authenticatedFlag) {
00380         e->authenticatedFlag = authenticated;
00381 #else
00382     if (authenticated) {
00383 #endif
00384         e->engineTime = engine_time;
00385         e->engineBoot = engineboot;
00386         e->lastReceivedEngineTime = snmpv3_local_snmpEngineTime();
00387     }
00388 
00389     e = NULL;                   /* Indicates a successful update. */
00390 
00391     DEBUGMSGTL(("lcd_set_enginetime", "engineID "));
00392     DEBUGMSGHEX(("lcd_set_enginetime", engineID, engineID_len));
00393     DEBUGMSG(("lcd_set_enginetime", ": boots=%d, time=%d\n", engineboot,
00394               engine_time));
00395 
00396   set_enginetime_quit:
00397     SNMP_FREE(e);
00398 
00399     return rval;
00400 
00401 }                               /* end set_enginetime() */
00402 
00403 
00404 
00405 
00406 /*******************************************************************-o-******
00407  * search_enginetime_list
00408  *
00409  * Parameters:
00410  *      *engineID
00411  *       engineID_len
00412  *      
00413  * Returns:
00414  *      Pointer to a etimelist record with engineID <engineID>  -OR-
00415  *      NULL if no record exists.
00416  *
00417  *
00418  * Search etimelist for an entry with engineID.
00419  *
00420  * ASSUMES that no engineID will have more than one record in the list.
00421  */
00422 Enginetime
00423 search_enginetime_list(u_char * engineID, u_int engineID_len)
00424 {
00425     int             rval = SNMPERR_SUCCESS;
00426     Enginetime      e = NULL;
00427 
00428 
00429     /*
00430      * Sanity check.
00431      */
00432     if (!engineID || (engineID_len <= 0)) {
00433         QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit);
00434     }
00435 
00436 
00437     /*
00438      * Find the entry for engineID if there be one.
00439      */
00440     rval = hash_engineID(engineID, engineID_len);
00441     if (rval < 0) {
00442         QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit);
00443     }
00444     e = etimelist[rval];
00445 
00446     for ( /*EMPTY*/; e; e = e->next) {
00447         if ((engineID_len == e->engineID_len)
00448             && !memcmp(e->engineID, engineID, engineID_len)) {
00449             break;
00450         }
00451     }
00452 
00453 
00454   search_enginetime_list_quit:
00455     return e;
00456 
00457 }                               /* end search_enginetime_list() */
00458 
00459 
00460 
00461 
00462 
00463 /*******************************************************************-o-******
00464  * hash_engineID
00465  *
00466  * Parameters:
00467  *      *engineID
00468  *       engineID_len
00469  *      
00470  * Returns:
00471  *      >0                      etimelist index for this engineID.
00472  *      SNMPERR_GENERR          Error.
00473  *      
00474  * 
00475  * Use a cheap hash to build an index into the etimelist.  Method is 
00476  * to hash the engineID, then split the hash into u_int's and add them up
00477  * and modulo the size of the list.
00478  *
00479  */
00480 int
00481 hash_engineID(u_char * engineID, u_int engineID_len)
00482 {
00483     int             rval = SNMPERR_GENERR;
00484     size_t          buf_len = SNMP_MAXBUF;
00485     u_int           additive = 0;
00486     u_char         *bufp, buf[SNMP_MAXBUF];
00487     void           *context = NULL;
00488 
00489 
00490 
00491     /*
00492      * Sanity check.
00493      */
00494     if (!engineID || (engineID_len <= 0)) {
00495         QUITFUN(SNMPERR_GENERR, hash_engineID_quit);
00496     }
00497 
00498 
00499     /*
00500      * Hash engineID into a list index.
00501      */
00502 #ifndef NETSNMP_DISABLE_MD5
00503     rval = sc_hash(usmHMACMD5AuthProtocol,
00504                    sizeof(usmHMACMD5AuthProtocol) / sizeof(oid),
00505                    engineID, engineID_len, buf, &buf_len);
00506 #else
00507     rval = sc_hash(usmHMACSHA1AuthProtocol,
00508                    sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid),
00509                    engineID, engineID_len, buf, &buf_len);
00510 #endif
00511     QUITFUN(rval, hash_engineID_quit);
00512 
00513     for (bufp = buf; (bufp - buf) < (int) buf_len; bufp += 4) {
00514         additive += (u_int) * bufp;
00515     }
00516 
00517   hash_engineID_quit:
00518     SNMP_FREE(context);
00519     memset(buf, 0, SNMP_MAXBUF);
00520 
00521     return (rval < 0) ? rval : (additive % ETIMELIST_SIZE);
00522 
00523 }                               /* end hash_engineID() */
00524 
00525 
00526 
00527 
00528 #ifdef NETSNMP_ENABLE_TESTING_CODE
00529 /*******************************************************************-o-******
00530  * dump_etimelist_entry
00531  *
00532  * Parameters:
00533  *      e
00534  *      count
00535  */
00536 void
00537 dump_etimelist_entry(Enginetime e, int count)
00538 {
00539     u_int           buflen;
00540     char            tabs[SNMP_MAXBUF], *t = tabs, *s;
00541 
00542 
00543 
00544     count += 1;
00545     while (count--) {
00546         t += sprintf(t, "  ");
00547     }
00548 
00549 
00550     buflen = e->engineID_len;
00551 #ifdef NETSNMP_ENABLE_TESTING_CODE
00552     if (!(s = dump_snmpEngineID(e->engineID, &buflen))) {
00553 #endif
00554         binary_to_hex(e->engineID, e->engineID_len, &s);
00555 #ifdef NETSNMP_ENABLE_TESTING_CODE
00556     }
00557 #endif
00558 
00559     DEBUGMSGTL(("dump_etimelist", "%s\n", tabs));
00560     DEBUGMSGTL(("dump_etimelist", "%s%s (len=%d) <%d,%d>\n", tabs,
00561                 s, e->engineID_len, e->engineTime, e->engineBoot));
00562     DEBUGMSGTL(("dump_etimelist", "%s%ld (%ld)", tabs,
00563                 e->lastReceivedEngineTime,
00564                 snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime));
00565 
00566     SNMP_FREE(s);
00567 
00568 }                               /* end dump_etimelist_entry() */
00569 
00570 
00571 
00572 
00573 /*******************************************************************-o-******
00574  * dump_etimelist
00575  */
00576 void
00577 dump_etimelist(void)
00578 {
00579     int             iindex = -1, count = 0;
00580     Enginetime      e;
00581 
00582 
00583 
00584     DEBUGMSGTL(("dump_etimelist", "\n"));
00585 
00586     while (++iindex < ETIMELIST_SIZE) {
00587         DEBUGMSG(("dump_etimelist", "[%d]", iindex));
00588 
00589         count = 0;
00590         e = etimelist[iindex];
00591 
00592         while (e) {
00593             dump_etimelist_entry(e, count++);
00594             e = e->next;
00595         }
00596 
00597         if (count > 0) {
00598             DEBUGMSG(("dump_etimelist", "\n"));
00599         }
00600     }                           /* endwhile */
00601 
00602     DEBUGMSG(("dump_etimelist", "\n"));
00603 
00604 }                               /* end dump_etimelist() */
00605 #endif                          /* NETSNMP_ENABLE_TESTING_CODE */