/* * Modification History * * 2003-October-10 Jason Rohrer * Created. * * 2003-October-12 Jason Rohrer * Fixed bugs and leaks. * Added recursive list printing function. * * 2003-November-18 Jason Rohrer * Added runtime setting for message history log. */ #include "MessageIDTracker.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SettingsManager.h" #include // Incoming IDs that have counters far beyond our largest counter // are seen as "too new" (probably an attack?) and not counted as fresh unsigned int MESSAGE_COUNTER_TOO_NEW_INCREMENT = 1000000; // maximum unsigned int unsigned int MAX_MESSAGE_COUNTER = 4294967295U; unsigned int getCounterDistance( unsigned int inCounterA, unsigned int inCounterB, char *outABelowB ) { unsigned int firstDistance; char firstABelowB; if( inCounterA < inCounterB ) { firstDistance = inCounterB - inCounterA; firstABelowB = true; } else { firstDistance = inCounterA - inCounterB; firstABelowB = false; } unsigned int secondDistance; char secondABelowB; if( inCounterA < inCounterB ) { secondDistance = MAX_MESSAGE_COUNTER - inCounterB + inCounterA; secondABelowB = false; } else { secondDistance = MAX_MESSAGE_COUNTER - inCounterA + inCounterB; secondABelowB = true; } // return smaller distance if( firstDistance < secondDistance ) { *outABelowB = firstABelowB; return firstDistance; } else { *outABelowB = secondABelowB; return secondDistance; } } MessageIDListItem::MessageIDListItem( MessageIDListItem *inNextItem, char *inUniqueName, unsigned int inCounter ) : mNextItem( inNextItem ), mUniqueName( stringDuplicate( inUniqueName ) ), mCounter( inCounter ) { } MessageIDListItem::~MessageIDListItem() { delete [] mUniqueName; } void MessageIDListItem::deleteAll() { if( mNextItem != NULL ) { mNextItem->deleteAll(); delete mNextItem; mNextItem = NULL; } } char MessageIDListItem::insertSorted( MessageIDListItem *inItem ) { if( mNextItem != NULL ) { char belowNext; getCounterDistance( inItem->mCounter, mNextItem->mCounter, &belowNext); if( belowNext ) { // inItem should go between us and our current next item // its next item is already NULL inItem->mNextItem = mNextItem; mNextItem = inItem; // inItem is not the last on the list return false; } else { // should go somewhere after our next item // recurse down list return mNextItem->insertSorted( inItem ); } } else { // we're the last on the list mNextItem = inItem; // inItem is now last item on list return true; } } char MessageIDListItem::nameExists( char *inUniqueName ) { if( strcmp( inUniqueName, mUniqueName ) == 0 ) { return true; } else { if( mNextItem != NULL ) { return mNextItem->nameExists( inUniqueName ); } else { // end of list, not found return false; } } } void MessageIDListItem::printListCounters( FILE *inFile ) { fprintf( inFile, "%u ", mCounter ); if( mNextItem != NULL ) { mNextItem->printListCounters( inFile ); } else { fprintf( inFile, "\n" ); } } MessageIDTracker::MessageIDTracker( int inCacheSize ) : mMaxCacheSize( inCacheSize ), mCurrentCacheSize( 0 ), mLock( new MutexLock() ), mListHead( NULL ), mFreshestCounter( 0 ), mTotalMessageCount( 0 ), mHistoryOutputFile( NULL ) { char found; int logMessageHistory = SettingsManager::getIntSetting( "logMessageHistory", &found ); if( logMessageHistory == 1 ) { mHistoryOutputFile = fopen( "messageHistory.log", "w" ); } } MessageIDTracker::~MessageIDTracker() { if( mListHead != NULL ) { mListHead->deleteAll(); delete mListHead; mListHead = NULL; } if( mHistoryOutputFile != NULL ) { fclose( mHistoryOutputFile ); } delete mLock; } char MessageIDTracker::checkIfIDFresh( char *inMessageID ) { mLock->lock(); if( mHistoryOutputFile != NULL ) { fprintf( mHistoryOutputFile, "%d %d %s", mTotalMessageCount, (int)( time( NULL ) ), inMessageID ); } char isFresh = false; // split into parts char *workingID = stringDuplicate( inMessageID ); char *pointerToDelim = strstr( workingID, "_" ); char *uniqueName; unsigned int counter; char idPartsFound = false; if( pointerToDelim != NULL ) { // terminate pointerToDelim[0] = '\0'; uniqueName = workingID; char *counterString = &( pointerToDelim[1] ); int numRead = sscanf( counterString, "%u", &counter ); if( numRead == 1 ) { idPartsFound = true; } } if( idPartsFound ) { if( mListHead == NULL ) { // empty list, all are fresh isFresh = true; mListHead = new MessageIDListItem( NULL, uniqueName, counter ); mCurrentCacheSize++; mFreshestCounter = counter; } else { // list not empty // check if name already exists if( ! mListHead->nameExists( uniqueName ) ) { if( mCurrentCacheSize < mMaxCacheSize ) { // room in list, all are fresh isFresh = true; if( counter <= mListHead->mCounter ) { // new head MessageIDListItem *newItem = new MessageIDListItem( mListHead, uniqueName, counter ); mListHead = newItem; mCurrentCacheSize++; // no change to freshest counter } else { // somewhere after head MessageIDListItem *newItem = new MessageIDListItem( NULL, uniqueName, counter ); char isNewTail = mListHead->insertSorted( newItem ); mCurrentCacheSize++; if( isNewTail ) { // new item has freshest counter mFreshestCounter = counter; } } } else { // cache full // make sure ID not too new char belowFreshest; unsigned int distFromFreshest = getCounterDistance( counter, mFreshestCounter, &belowFreshest ); if( belowFreshest || ( !belowFreshest && distFromFreshest < MESSAGE_COUNTER_TOO_NEW_INCREMENT ) ) { char belowHead; getCounterDistance( counter, mListHead->mCounter, &belowHead ); if( belowHead ) { // don't even bother inserting } else { // insert and then delete the smallest (the head) MessageIDListItem *newItem = new MessageIDListItem( NULL, uniqueName, counter ); char isNewTail = mListHead->insertSorted( newItem ); // delete head MessageIDListItem *oldHead = mListHead; mListHead = mListHead->mNextItem; delete oldHead; // no change in cache size if( isNewTail ) { mFreshestCounter = counter; } isFresh = true; } // end else not below head } // end if not too new } // end else cache full } // end if name does not already exist in cache } // end else list not empty } // end if parts found delete [] workingID; if( mHistoryOutputFile != NULL ) { if( isFresh ) { fprintf( mHistoryOutputFile, " [fresh]\n" ); } else { fprintf( mHistoryOutputFile, " [expired]\n" ); } fprintf( mHistoryOutputFile, "Current sorted ID cache:\n" ); if( mListHead != NULL ) { mListHead->printListCounters( mHistoryOutputFile ); } else { fprintf( mHistoryOutputFile, "empty\n" ); } fflush( mHistoryOutputFile ); } mTotalMessageCount++; mLock->unlock(); return isFresh; } unsigned int MessageIDTracker::getFreshCounter() { mLock->lock(); unsigned int newID; if( mFreshestCounter < MAX_MESSAGE_COUNTER ) { newID = mFreshestCounter + 1; } else { // wrap around newID = 0; } mLock->unlock(); return newID; }