/* * lcd_time.c * * XXX Should etimelist entries with <0,0> time tuples be timed out? * XXX Need a routine to free the memory? (Perhaps at shutdown?) */ #include #include #include #include #ifdef HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #else #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #ifdef HAVE_NETINET_IN_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_DMALLOC_H #include #endif #include #include #include #include #include #include #include #include #include #include #include netsnmp_feature_child_of(usm_support, libnetsnmp) netsnmp_feature_child_of(usm_lcd_time, usm_support) #ifndef NETSNMP_FEATURE_REMOVE_USM_LCD_TIME /* * Global static hashlist to contain Enginetime entries. * * New records are prepended to the appropriate list at the hash index. */ static Enginetime etimelist[ETIMELIST_SIZE]; /*******************************************************************-o-****** * get_enginetime * * Parameters: * *engineID * engineID_len * *engineboot * *engine_time * * Returns: * SNMPERR_SUCCESS Success -- when a record for engineID is found. * SNMPERR_GENERR Otherwise. * * * Lookup engineID and return the recorded values for the * tuple adjusted to reflect the estimated time * at the engine in question. * * Special case: if engineID is NULL or if engineID_len is 0 then * the time tuple is returned immediately as zero. * * XXX What if timediff wraps? >shrug< * XXX Then: you need to increment the boots value. Now. Detecting * this is another matter. */ int get_enginetime(const u_char * engineID, u_int engineID_len, u_int * engineboot, u_int * engine_time, u_int authenticated) { int rval = SNMPERR_SUCCESS; int timediff = 0; Enginetime e = NULL; /* * Sanity check. */ if (!engine_time || !engineboot) { QUITFUN(SNMPERR_GENERR, get_enginetime_quit); } /* * Compute estimated current engine_time tuple at engineID if * a record is cached for it. */ *engine_time = *engineboot = 0; if (!engineID || (engineID_len <= 0)) { QUITFUN(SNMPERR_GENERR, get_enginetime_quit); } if (!(e = search_enginetime_list(engineID, engineID_len))) { QUITFUN(SNMPERR_GENERR, get_enginetime_quit); } #ifdef LCD_TIME_SYNC_OPT if (!authenticated || e->authenticatedFlag) { #endif *engine_time = e->engineTime; *engineboot = e->engineBoot; timediff = (int) (snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime); #ifdef LCD_TIME_SYNC_OPT } #endif if (timediff > (int) (ENGINETIME_MAX - *engine_time)) { *engine_time = (timediff - (ENGINETIME_MAX - *engine_time)); /* * FIX -- move this check up... should not change anything * * if engineboot is already locked. ??? */ if (*engineboot < ENGINEBOOT_MAX) { *engineboot += 1; } } else { *engine_time += timediff; } DEBUGMSGTL(("lcd_get_enginetime", "engineID ")); DEBUGMSGHEX(("lcd_get_enginetime", engineID, engineID_len)); DEBUGMSG(("lcd_get_enginetime", ": boots=%d, time=%d\n", *engineboot, *engine_time)); get_enginetime_quit: return rval; } /* end get_enginetime() */ /*******************************************************************-o-****** * get_enginetime * * Parameters: * *engineID * engineID_len * *engineboot * *engine_time * * Returns: * SNMPERR_SUCCESS Success -- when a record for engineID is found. * SNMPERR_GENERR Otherwise. * * * Lookup engineID and return the recorded values for the * tuple adjusted to reflect the estimated time * at the engine in question. * * Special case: if engineID is NULL or if engineID_len is 0 then * the time tuple is returned immediately as zero. * * XXX What if timediff wraps? >shrug< * XXX Then: you need to increment the boots value. Now. Detecting * this is another matter. */ int get_enginetime_ex(u_char * engineID, u_int engineID_len, u_int * engineboot, u_int * engine_time, u_int * last_engine_time, u_int authenticated) { int rval = SNMPERR_SUCCESS; int timediff = 0; Enginetime e = NULL; /* * Sanity check. */ if (!engine_time || !engineboot || !last_engine_time) { QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit); } /* * Compute estimated current engine_time tuple at engineID if * a record is cached for it. */ *last_engine_time = *engine_time = *engineboot = 0; if (!engineID || (engineID_len <= 0)) { QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit); } if (!(e = search_enginetime_list(engineID, engineID_len))) { QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit); } #ifdef LCD_TIME_SYNC_OPT if (!authenticated || e->authenticatedFlag) { #endif *last_engine_time = *engine_time = e->engineTime; *engineboot = e->engineBoot; timediff = (int) (snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime); #ifdef LCD_TIME_SYNC_OPT } #endif if (timediff > (int) (ENGINETIME_MAX - *engine_time)) { *engine_time = (timediff - (ENGINETIME_MAX - *engine_time)); /* * FIX -- move this check up... should not change anything * * if engineboot is already locked. ??? */ if (*engineboot < ENGINEBOOT_MAX) { *engineboot += 1; } } else { *engine_time += timediff; } DEBUGMSGTL(("lcd_get_enginetime_ex", "engineID ")); DEBUGMSGHEX(("lcd_get_enginetime_ex", engineID, engineID_len)); DEBUGMSG(("lcd_get_enginetime_ex", ": boots=%d, time=%d\n", *engineboot, *engine_time)); get_enginetime_ex_quit: return rval; } /* end get_enginetime_ex() */ void free_enginetime(unsigned char *engineID, size_t engineID_len) { Enginetime e = NULL; int rval = 0; rval = hash_engineID(engineID, engineID_len); if (rval < 0) return; e = etimelist[rval]; while (e != NULL) { etimelist[rval] = e->next; SNMP_FREE(e->engineID); SNMP_FREE(e); e = etimelist[rval]; } } /*******************************************************************-o-**** ** * free_etimelist * * Parameters: * None * * Returns: * void * * * Free all of the memory used by entries in the etimelist. * */ void free_etimelist(void) { int index = 0; Enginetime e = NULL; Enginetime nextE = NULL; for( ; index < ETIMELIST_SIZE; ++index) { e = etimelist[index]; while(e != NULL) { nextE = e->next; SNMP_FREE(e->engineID); SNMP_FREE(e); e = nextE; } etimelist[index] = NULL; } return; } /*******************************************************************-o-****** * set_enginetime * * Parameters: * *engineID * engineID_len * engineboot * engine_time * * Returns: * SNMPERR_SUCCESS Success. * SNMPERR_GENERR Otherwise. * * * Lookup engineID and store the given tuple * and then stamp the record with a consistent source of local time. * If the engineID record does not exist, create one. * * Special case: engineID is NULL or engineID_len is 0 defines an engineID * that is "always set." * * XXX "Current time within the local engine" == time(NULL)... */ int set_enginetime(const u_char * engineID, u_int engineID_len, u_int engineboot, u_int engine_time, u_int authenticated) { int rval = SNMPERR_SUCCESS, iindex; Enginetime e = NULL; /* * Sanity check. */ if (!engineID || (engineID_len <= 0)) { return rval; } /* * Store the given tuple in the record * for engineID. Create a new record if necessary. */ if (!(e = search_enginetime_list(engineID, engineID_len))) { if ((iindex = hash_engineID(engineID, engineID_len)) < 0) { QUITFUN(SNMPERR_GENERR, set_enginetime_quit); } e = (Enginetime) calloc(1, sizeof(*e)); e->next = etimelist[iindex]; etimelist[iindex] = e; e->engineID = (u_char *) calloc(1, engineID_len); memcpy(e->engineID, engineID, engineID_len); e->engineID_len = engineID_len; } #ifdef LCD_TIME_SYNC_OPT if (authenticated || !e->authenticatedFlag) { e->authenticatedFlag = authenticated; #else if (authenticated) { #endif e->engineTime = engine_time; e->engineBoot = engineboot; e->lastReceivedEngineTime = snmpv3_local_snmpEngineTime(); } e = NULL; /* Indicates a successful update. */ DEBUGMSGTL(("lcd_set_enginetime", "engineID ")); DEBUGMSGHEX(("lcd_set_enginetime", engineID, engineID_len)); DEBUGMSG(("lcd_set_enginetime", ": boots=%d, time=%d\n", engineboot, engine_time)); set_enginetime_quit: SNMP_FREE(e); return rval; } /* end set_enginetime() */ /*******************************************************************-o-****** * search_enginetime_list * * Parameters: * *engineID * engineID_len * * Returns: * Pointer to a etimelist record with engineID -OR- * NULL if no record exists. * * * Search etimelist for an entry with engineID. * * ASSUMES that no engineID will have more than one record in the list. */ Enginetime search_enginetime_list(const u_char * engineID, u_int engineID_len) { int rval = SNMPERR_SUCCESS; Enginetime e = NULL; /* * Sanity check. */ if (!engineID || (engineID_len <= 0)) { QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit); } /* * Find the entry for engineID if there be one. */ rval = hash_engineID(engineID, engineID_len); if (rval < 0) { QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit); } e = etimelist[rval]; for ( /*EMPTY*/; e; e = e->next) { if ((engineID_len == e->engineID_len) && !memcmp(e->engineID, engineID, engineID_len)) { break; } } search_enginetime_list_quit: return e; } /* end search_enginetime_list() */ /*******************************************************************-o-****** * hash_engineID * * Parameters: * *engineID * engineID_len * * Returns: * >0 etimelist index for this engineID. * SNMPERR_GENERR Error. * * * Use a cheap hash to build an index into the etimelist. Method is * to hash the engineID, then split the hash into u_int's and add them up * and modulo the size of the list. * */ int hash_engineID(const u_char * engineID, u_int engineID_len) { int rval = SNMPERR_GENERR; size_t buf_len = SNMP_MAXBUF; u_int additive = 0; u_char *bufp, buf[SNMP_MAXBUF]; void *context = NULL; /* * Sanity check. */ if (!engineID || (engineID_len <= 0)) { QUITFUN(SNMPERR_GENERR, hash_engineID_quit); } /* * Hash engineID into a list index. */ #ifndef NETSNMP_DISABLE_MD5 rval = sc_hash(usmHMACMD5AuthProtocol, sizeof(usmHMACMD5AuthProtocol) / sizeof(oid), engineID, engineID_len, buf, &buf_len); if (rval == SNMPERR_SC_NOT_CONFIGURED) { /* fall back to sha1 */ rval = sc_hash(usmHMACSHA1AuthProtocol, sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid), engineID, engineID_len, buf, &buf_len); } #else rval = sc_hash(usmHMACSHA1AuthProtocol, sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid), engineID, engineID_len, buf, &buf_len); #endif QUITFUN(rval, hash_engineID_quit); for (bufp = buf; (bufp - buf) < (int) buf_len; bufp += 4) { additive += (u_int) * bufp; } hash_engineID_quit: SNMP_FREE(context); memset(buf, 0, SNMP_MAXBUF); return (rval < 0) ? rval : (int)(additive % ETIMELIST_SIZE); } /* end hash_engineID() */ #ifdef NETSNMP_ENABLE_TESTING_CODE /*******************************************************************-o-****** * dump_etimelist_entry * * Parameters: * e * count */ void dump_etimelist_entry(Enginetime e, int count) { size_t buflen; char tabs[SNMP_MAXBUF], *t = tabs, *s; count += 1; while (count--) { t += sprintf(t, " "); } buflen = e->engineID_len; if (!(s = dump_snmpEngineID(e->engineID, &buflen))) { binary_to_hex(e->engineID, e->engineID_len, &s); } DEBUGMSGTL(("dump_etimelist", "%s\n", tabs)); DEBUGMSGTL(("dump_etimelist", "%s%s (len=%d) <%d,%d>\n", tabs, s, e->engineID_len, e->engineTime, e->engineBoot)); DEBUGMSGTL(("dump_etimelist", "%s%ld (%ld)", tabs, e->lastReceivedEngineTime, snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime)); SNMP_FREE(s); } /* end dump_etimelist_entry() */ /*******************************************************************-o-****** * dump_etimelist */ void dump_etimelist(void) { int iindex = -1, count = 0; Enginetime e; DEBUGMSGTL(("dump_etimelist", "\n")); while (++iindex < ETIMELIST_SIZE) { DEBUGMSG(("dump_etimelist", "[%d]", iindex)); count = 0; e = etimelist[iindex]; while (e) { dump_etimelist_entry(e, count++); e = e->next; } if (count > 0) { DEBUGMSG(("dump_etimelist", "\n")); } } /* endwhile */ DEBUGMSG(("dump_etimelist", "\n")); } /* end dump_etimelist() */ #endif /* NETSNMP_ENABLE_TESTING_CODE */ #endif /* NETSNMP_FEATURE_REMOVE_USM_LCD_TIME */