// snfNETmgr.cpp // // (C) Copyright 2006 - 2009 ARM Research Labs, LLC // See www.armresearch.com for the copyright terms. // // See snfNETmgr.hpp for details. #include #include #include #include #include #include #include #include #include "SNFMulti/snfNETmgr.hpp" #include "SNFMulti/snf_sync.hpp" #include "CodeDweller/mangler.hpp" #include "CodeDweller/base64codec.hpp" // #include "tcp_watchdog.hpp" No longer using TCPWatchdog -- see below _M using namespace std; using namespace CodeDweller; namespace SNFMulti { ///// utilities //////////////////////////////////////////////////////////////// const int MSecsInSecs = 1000; // Multiplier - seconds to milliseconds. unsigned long long int SecsAsMSecs(unsigned int Secs) { return (MSecsInSecs * Secs); } //// snfNETmgr ///////////////////////////////////////////////////////////////// const ThreadType snfNETmgr::Type("snfNETManager"); // The thread's type. const ThreadState snfNETmgr::Sleeping("Sleeping"); // Taking a break. const ThreadState snfNETmgr::SYNC_Connect("Connecting"); // Connecting to SYNC server. const ThreadState snfNETmgr::SYNC_Read_Challenge("Reading challenge"); // Reading challenge. const ThreadState snfNETmgr::SYNC_Compute_Response("Computing crypto"); // Computing crypto response. const ThreadState snfNETmgr::SYNC_Send_Response("Sending crypto"); // Sending crypto response. const ThreadState snfNETmgr::SYNC_Read_Availabilty("Reading Availability"); // Reading rulebase status. const ThreadState snfNETmgr::SYNC_Send_GBUdb_Alerts("Sending GBUdb"); // Sending GBUdb alerts. const ThreadState snfNETmgr::SYNC_Send_Status_Reports("Sending Status"); // Sending status reports. const ThreadState snfNETmgr::SYNC_Send_Samples("Sending Samples"); // Sending message samples. const ThreadState snfNETmgr::SYNC_Send_End_Of_Report("Sending End"); // Sending end of client data. const ThreadState snfNETmgr::SYNC_Read_Server_Response("Reading Server"); // Reading server data. const ThreadState snfNETmgr::SYNC_Close_Connection("Closing Connection"); // Closing connection. const ThreadState snfNETmgr::SYNC_Parse_GBUdb_Reflections("Parsing GBUdb"); // Parsing GBUdb reflections. const ThreadState snfNETmgr::SYNC_Log_Event("Logging SYNC"); // Logging SYNC event. snfNETmgr::snfNETmgr() : // Starting up the NETmgr Thread(snfNETmgr::Type, "NET Manager"), // Network manager and Name. myLOGmgr(NULL), // No LOGmgr yet. isTimeToStop(false), // Not time to stop yet. isConfigured(false), // Not configured yet. SYNCTimer(30000), // Sync every 30 secs by default. SyncSecsOverride(-1) { // Override is -1 (off) by default. run(); // Run the thread. } snfNETmgr::~snfNETmgr() { // On descruction, NETmgr must stop(); // Stop it's thread (if not already) myLOGmgr = NULL; // Clear out the LOGmgr hookup isConfigured = false; // and the configured flag. } void snfNETmgr::stop() { // The stop method... if(!isTimeToStop) { // only does it's work once: isTimeToStop = true; // tells it's thread to stop join(); // and waits for it to shut down. } } void snfNETmgr::myTask() { // Here's the thread task. Sleeper WaitASecond(1000); // Heartbeat timer. while(false == isTimeToStop) { // Until it's time to stop, CurrentThreadState(Sleeping); // post our status, WaitASecond(); // pause for a second, if(isConfigured) { // then poll our tasks. // Do stuff here that requires configuration data. if(SYNCTimer.isExpired()) { sync(); SYNCTimer.restart(); } // If it's time to sync - do it :-) } } } void snfNETmgr::linkLOGmgr(snfLOGmgr& L) { // Set the LOGmgr. myLOGmgr = &L; } void snfNETmgr::linkGBUdbmgr(snfGBUdbmgr& G) { // Set the GBUdbmgr. myGBUdbmgr = &G; } // In theory, configure will get called each time the rulebase manager loads // a new configuration / rulebase. The configure() method updates the bits of // NETmgr that run background tasks. Live-Data tasks pass their grab()bed // CFGData object in order to maintain self-consistency. void snfNETmgr::configure(snfCFGData& CFGData) { // Update the configuration. ScopeMutex CFGDataExchange(ConfigMutex); // Lock the config data during updates. // Update the internal config data from CFGData while we are locked. // Internal functions which depend on this data will lock the object, // grab the bits they depend upon for that pass, and then unlock. RulebaseFilePath = CFGData.RuleFilePath; // Where we can find our rulebase? SyncHostName = CFGData.network_sync_host; // Where do we connect to sync? SyncHostPort = CFGData.network_sync_port; // What port do we use to sync? HandshakeFilePath = CFGData.paths_workspace_path + ".handshake"; // Where we store our handshake. UpdateReadyFilePath = CFGData.paths_workspace_path + "UpdateReady.txt"; // Where we put update trigger files. SyncSecsConfigured = CFGData.network_sync_secs; // Capture the configured sync time. if(0 > SyncSecsOverride) { // If the sync timer isn't in override, if(SYNCTimer.getDuration() != SecsAsMSecs(SyncSecsConfigured)) { // And the config time is different than SYNCTimer.setDuration(SecsAsMSecs(SyncSecsConfigured)); // the timer's current setting then set } // the timer to the new value. } // If we are in override, timer is set. License = CFGData.node_licenseid; // Capture our node id (license id). SecurityKey = CFGData.SecurityKey; // Capture our security key. evolvePad(CFGData.SecurityKey); // Seed our Pad generator with it. // Safety check before turning this on ;-) if( NULL != myLOGmgr && NULL != myGBUdbmgr ) { // If we are properly linked then isConfigured = true; // at this point we are configured! } } void snfNETmgr::sendSample( // Send a sampled message... snfCFGData& CFGData, // Use this configuration, snfScanData& ScanData, // Include this scan data, const unsigned char* MessageBuffer, // This is the message itself int MessageLength // and it is this size. ) { string TimeStamp; (*myLOGmgr).Timestamp(TimeStamp); // Grab a timestamp. ostringstream XML; // Make formatting easier with this. //-- XML << "" << endl; //-- XML << "" << endl; //-- as many as needed if(0 < ScanData.MatchRecords.size()) { // If we have match records - emit them. list::iterator iM; // Grab an iterator. for( // Emit each snf_match entry. iM = ScanData.MatchRecords.begin(); iM != ScanData.MatchRecords.end(); iM++) { XML << ""; } } //-- XML << "" << endl; // Starting with the msg element. to_base64 EncodedMessageData( reinterpret_cast(MessageBuffer), MessageLength); // Encode the message to base64. const int SampleLineLength = 64; // 64 bytes per line is good. for(int i = 0; i < MessageLength;) { // Now we break it into lines for(int l = 0; l < SampleLineLength && i < MessageLength; l++, i++) { // that are a reasonable length. XML << EncodedMessageData.at(i); // Emit one character at a time... } // At the end of a reasonable XML << endl; // length we terminate the line. } XML << "" << endl; // End of the element. //-- done with the sample! XML << "" << endl; // Last thing we do is post the formatted string to the buffer. const unsigned int SampleSafetyLimit = 100000; // 100 Kbyte limit on samples. ScopeMutex DoNotDisturb(myMutex); // Don't bug me man I'm busy. if(SampleSafetyLimit < SamplesBuffer.length()) // If the samples buffer is full SamplesBuffer.clear(); // clear it before adding more. SamplesBuffer.append(XML.str()); // Append the XML to the buffer. } string snfNETmgr::getSamples() { // Synchronized way to get Samples. ScopeMutex DoNotDisturb(myMutex); // Lock the mutex to protect our work. string SamplesBatch = SamplesBuffer; // Copy the samples to a new string. SamplesBuffer.clear(); // Clear the samples buffer. return SamplesBatch; // Return a batch of Samples. } void snfNETmgr::sendReport(const string& S) { // How to send a status report. const unsigned int ReportSafetyLimit = 100000; // 100 Kbytes limit on reports. ScopeMutex DoNotDisturb(myMutex); // Lock the mutex for a moment. if(ReportSafetyLimit < ReportsBuffer.length()) // If the reports buffer is full ReportsBuffer.clear(); // clear it before adding more. ReportsBuffer.append(S); // Append the report. } string snfNETmgr::getReports() { // Synchronized way to get Reports. ScopeMutex DoNotDisturb(myMutex); // Lock the mutex to protect our work. string ReportsBatch = ReportsBuffer; // Copy the reports to a new string. ReportsBuffer.clear(); // Clear the reports buffer. return ReportsBatch; // Return a batch of Reports. } string& snfNETmgr::RulebaseUTC(string& t) { // Gets local rulebase file UTC. struct stat RulebaseStat; // First we need a stat buffer. if(0 != stat(RulebaseFilePath.c_str(), &RulebaseStat)) { // If we can't get the stat we t.append("000000000000"); return t; // will return 000000000000 to } // make sure we should get the file. struct tm RulebaseTime; // Allocate a time structure. RulebaseTime = *(gmtime(&RulebaseStat.st_mtime)); // Copy the file time to it as UTC. char TimestampBfr[20]; // Timestamp buffer. (void) snprintf( // Format yyyymmddhhmmss TimestampBfr, sizeof(TimestampBfr), "%04d%02d%02d%02d%02d%02d", RulebaseTime.tm_year+1900, RulebaseTime.tm_mon+1, RulebaseTime.tm_mday, RulebaseTime.tm_hour, RulebaseTime.tm_min, RulebaseTime.tm_sec ); t.append(TimestampBfr); // Append the timestamp to t return t; // and return it to the caller. } unsigned long snfNETmgr::ResolveHostIPFromName(const string& N) { // Host name resolution tool. ScopeMutex OneAtATimePlease(ResolverMutex); // Resolve only one at a time. unsigned long IP = inet_addr(N.c_str()); // See if it's an IP. if (INADDR_NONE == IP) { // If it's not an IP resolve it. hostent* H = gethostbyname(N.c_str()); // Resolve the host. if (NULL == H) { // If we didn't get a resolution return INADDR_NONE; // return no address. } // If we did resolve the address IP = *((unsigned long*)H->h_addr_list[0]); // get the primary entry. } return ntohl(IP); // Return what we got (host order) } // The Evolving One Time Pad engine is just slightly better than calling // rand() with the system time as a seed. However, it does have the advantage // that in order to guess it's initial state an attacker would need to already // know the license id and authentication. It also has the advantage that it // adds small amounts of entropy over time and never really forgets them. For // example, the exact time between calls to evolvePad is dependent on how long // it takes to sync which is dependent on how much data there is to report // which is dependent on the number and size of messages scanned etc... and // this is also impacted a bit by network performance issues during the sync. // Sensitivity to this entropy has millisecond resolution. This is a cross- // platform solution that depends only on our own code ;-) void snfNETmgr::evolvePad(string Entropy) { // Add entropy and evolve. ScopeMutex OneAtATimePlease(PadMutex); // Protect the one time pad. myLOGmgr->Timestamp(Entropy); // Time matters ;-) for(unsigned int a = 0; a < Entropy.length(); a++) { // Add the entropy to our generator. PadGenerator.Encrypt(Entropy.at(a)); } msclock rt = myLOGmgr->RunningTime(); // Get the elapsed running time so far. unsigned char* rtb = reinterpret_cast(&rt); // Convert that long long into bytes. for(unsigned int a = 0; a < sizeof(msclock); a++) { // Encrypt those bytes one by one PadGenerator.Encrypt(rtb[a]); // to add more entropy. } } // To get a pad of any length you like, use the OneTimePad() // Note that we don't assign a value to x before using it! If we get lucky, // we will get some random value from ram as additional entropy ;-) If we end // up starting with zero, that's ok too. PadBuffer snfNETmgr::OneTimePad(int Len) { // Get Len bytes of one time pad. PadBuffer B; // Start with a buffer. B.reserve(Len); // Reserve Len bytes. unsigned char x = PadGenerator.Encrypt(0); // Get an unexposed byte to start with. for(int a = 0; a < Len; a++) { // Create Len bytes of pad by evolving B.push_back(x = PadGenerator.Encrypt(x)); // x through itself and copying the } // data into the buffer. return B; // Return the result. } // Handshake tries to return the current stored handshake. If it can't then it // returns a new handshake based on data from the pad generator. PadBuffer snfNETmgr::Handshake() { // What is the current handshake? if(CurrentHandshake.size() != SNFHandshakeSize) { // If we don't have one make one! CurrentHandshake = OneTimePad(SNFHandshakeSize); // Set up a default handshake to use try { // if we can't remember the real one. ifstream HSF(HandshakeFilePath.c_str(), ios::binary); // Open the handshake file. char* bfr = reinterpret_cast(&CurrentHandshake[0]); // Manufacture a proper pointer. HSF.read(bfr, SNFHandshakeSize); // Read the data (overwrite the HSB). HSF.close(); // Close the file. } catch(...) { } // Ignore any errors. } return CurrentHandshake; // Return the buffer. } PadBuffer& snfNETmgr::Handshake(PadBuffer& NewHandshake) { // Store a new handshake. CurrentHandshake = NewHandshake; // Grab the new handshake try { // then try to store it... ofstream HSF(HandshakeFilePath.c_str(), ios::binary | ios::trunc); // Open the handshake file. char* bfr = reinterpret_cast(&NewHandshake[0]); // Access the raw buffer. HSF.write(bfr, NewHandshake.size()); // Replace the old handshake HSF.close(); // close the file. } catch(...) {} // Ignore errors. return NewHandshake; // Return what we were given. } void snfNETmgr::postUpdateTrigger(string& updateUTC) { // Post an update trigger file. try { // Safely post an update trigger. ofstream HSF(UpdateReadyFilePath.c_str(), ios::binary | ios::trunc); // Open/create the trigger file. char* bfr = reinterpret_cast(&updateUTC[0]); // Access the raw UTC buffer. HSF.write(bfr, updateUTC.size()); // Write the update timestamp. HSF.close(); // close the file. } catch(...) {} // Ignore errors. } // Utility to read a line from a non-blocking TCPHost & check the timeout. const unsigned int MaxReadLineLength = 1024; // How long a line can be. string readLineTimeout(TCPHost& S, Timeout& T) { // Read a line from S until T. Sleeper WaitForMoreData(50); // How long to wait when no data. string LineBuffer = ""; // Buffer for the line. while( // Keep going as long as: false == T.isExpired() && // our timeout has not expired AND MaxReadLineLength > LineBuffer.length() // we haven't reached our limit. ) { char c = 0; // One byte at a time if(1 == S.receive(&c, sizeof(c))) { // Read from the TCPHost. LineBuffer.push_back(c); // Push the byte onto the string. if('\n' == c) break; // If it was a newline we're done! } else { // If we didn't get any data WaitForMoreData(); // pause before our next run. } } return LineBuffer; // Always return our buffer. } // Utility to write data to a non-blocking TCPHost & check the timeout. // Some networks can only handle small packets and fragmentation can be a // problem. Also, on Win* especially, sending small chunks is _MUCH_ more // reliable than trying to send large buffers all at once. SO - here we break // down our sending operations into medium sized chunks of data. The underlying // os can reorganize these chunks as needed for the outgouing stream. If the OS // needs us to slow down (doesn't send full chunks) then we introduce a small // delay between chunks to give the channel more time. const int MaxSendChunkSize = 512; // Size of one chunk in a write. void sendDataTimeout(TCPHost& S, Timeout& T, char* Bfr, int Len) { // Send and keep track of time. Sleeper WaitForMoreRoom(15); // Wait to send more data. int Remaining = Len; // This is how much we have left. while( // For as long as: false == T.isExpired() && // We still have time left AND 0 < Remaining // We still have data left ) { int ThisChunkSize = Remaining; // Hope to send it all in one chunk if(MaxSendChunkSize < ThisChunkSize) ThisChunkSize = MaxSendChunkSize; // but break it down as needed. int SentThisTime = S.transmit(Bfr, ThisChunkSize); // Send the data. How much went? Remaining -= SentThisTime; // Calculate how much is left. Bfr += SentThisTime; // Move our pointer (old school!) if(ThisChunkSize > SentThisTime) WaitForMoreRoom(); // If some of this chunk didn't go } // the pause before the next chunk. } void sendDataTimeout(TCPHost& S, Timeout& T, string& D) { // Send a string and keep track sendDataTimeout(S, T, const_cast(D.c_str()), D.length()); // of time. (Polymorphism is fun) } void snfNETmgr::sync() { // Synchronize with central command. // Keep these things in scope. This is how we roll. string HostName; int HostPort; string Secret; string Node; // Grab our configuration data (marchng orders). if(!isConfigured) return; // If we're not configured, don't! else { ScopeMutex GettingConfig(ConfigMutex); // Temporarily lock our config. HostName = SyncHostName; // We will connect to this host. HostPort = SyncHostPort; // We will connect to this port. Secret = SecurityKey; // Get the security key. Node = License; // Get the Node ID. } try { // Lots can go wrong so catch it :-) // 20080326 _M Blocking sockets tend to lock up so I've refactored this // code to use non-blocking sockets. This is actually part of the previous // refactor (TCPWatchdog see below) since without the watchdog there is no // way to get out of a blocking socket if it's dead. // 20080325 _M TCPWatchdog is a brute. It doesn't pay attention to thread // states. A weird bug showed up where the SYNC session seemed to hang and // the TCPWatchdog was left alive. In the process of hunting down this bug // I decided to remove the TCPWatchdog and put appropriate timeout checking // in each of the comms loops instead. So, from now on: // if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); const int SyncSessionTimeout = 2 * SYNCTimer.getDuration(); // Timeout is twice poll time. Timeout SessionDog(SyncSessionTimeout); // Give this long for a session. // Connect to the sync host. CurrentThreadState(SYNC_Connect); SocketAddress SyncHostAddress; // We'll need an address. SyncHostAddress.setPort(HostPort); // Set the port. SyncHostAddress.setAddress(ResolveHostIPFromName(HostName)); // Resolve and set the IP. TCPHost SyncServer(SyncHostAddress); // Set up a host connection. SyncServer.makeNonBlocking(); // Make the connection non-blocking. PollTimer WaitForOpen(10, 340); // Expand 10ms to 340ms between tries. while(!SessionDog.isExpired()) { // Wait & Watch for a good connection. try { SyncServer.open(); } // Try opening the connection. catch(exception& e) { // If we get an exception then string ConnectFailMessage = "snfNETmgr::sync().open() "; // format a useful message about ConnectFailMessage.append(e.what()); // the error and then throw throw SyncFailed(ConnectFailMessage); // a SyncFailed exception. } if(SyncServer.isOpen()) break; // When successful, let's Go! else WaitForOpen.pause(); // When not yet successful, pause } // then try again if we have time. if(!SyncServer.isOpen()) throw SyncFailed("Connect Timed Out"); // Check our connection. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. // Start communicating. string LineBuffer = ""; // Input Line Buffer. // Read challenge CurrentThreadState(SYNC_Read_Challenge); LineBuffer = readLineTimeout(SyncServer, SessionDog); // Read the challenge line. snf_sync Challenge(LineBuffer.c_str(), LineBuffer.length()); // Interpret what we read. if( // Check that it's good... Challenge.bad() || // A complete packet was read 0 >= Challenge.snf_sync_challenge_txt.length() // and the challenge is present. ) throw SyncFailed("sync() Challenge.bad()"); // If not then throw. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. // Write response CurrentThreadState(SYNC_Compute_Response); from_base64 DecodedChallenge(Challenge.snf_sync_challenge_txt); // Decode the challenge. //--- Prepare the secret. MANGLER ResponseGenerator; // Grab a mangler. for(unsigned int i = 0; i < Secret.length(); i++) // Fill it with the ResponseGenerator.Encrypt(Secret.at(i)); // security key. const int ManglerKeyExpansionCount = 1024; // Loop this many to randomize. for(int x = 0, i = 0; i < ManglerKeyExpansionCount; i++) // For the required number of loops, x = ResponseGenerator.Encrypt(x); // have Mangler chase it's tail. //--- Absorb the challenge. for(unsigned int i = 0; i < DecodedChallenge.size(); i++) // Evolve through the challenge. ResponseGenerator.Encrypt(DecodedChallenge.at(i)); /*** We now have half of the key for this session ***/ //--- Encrypt our Pad. PadBuffer NewPad = OneTimePad(); // Grab a new Pad (default size). base64buffer ResponseBin; // With the key now established, for(unsigned int i = 0; i < NewPad.size(); i++) // encrypt the one time pad for ResponseBin.push_back( // transfer. ResponseGenerator.Encrypt(NewPad[i])); //--- Encrypt our Handshake. PadBuffer CurrentHandshake = Handshake(); // Recall the secret handshake. for(unsigned int i = 0; i < CurrentHandshake.size(); i++) // Encrypt that into the stream. ResponseBin.push_back( ResponseGenerator.Encrypt(CurrentHandshake[i])); //--- Encrypt our Signature. for(unsigned int x = 0, i = 0; i < SNFSignatureSize; i++) // Generate a hash by having Mangler ResponseBin.push_back( // chase it's tail for the appropriate x = ResponseGenerator.Encrypt(x)); // number of bytes. //--- Encode our response as base64 and send it. to_base64 ResponseTxt(ResponseBin); // Encode the cyphertext as base64. string ResponseTxtString; // Create a handy string and place ResponseTxtString.assign(ResponseTxt.begin(), ResponseTxt.end()); // the base 64 text into it. string ResponseMsg; // Build an appropriate response ResponseMsg.append("\n"); // for authentication. CurrentThreadState(SYNC_Send_Response); sendDataTimeout(SyncServer, SessionDog, ResponseMsg); // Send the response. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. // Read rulebase info or error CurrentThreadState(SYNC_Read_Availabilty); LineBuffer = readLineTimeout(SyncServer, SessionDog); // Read the rulebase status line. snf_sync RulebaseResponse(LineBuffer.c_str(), LineBuffer.length()); // Interpret what we read. if( // Check that it's good... RulebaseResponse.bad() // A complete packet was read. ) throw SyncFailed("sync() Response.bad()"); // If not then throw. if(0 < RulebaseResponse.snf_sync_error_message.length()) { // If the response was an error PadBuffer NewNullHandshake; // then we will assume we are out NewNullHandshake.assign(SNFHandshakeSize, 0); // of sync with the server so we Handshake(NewNullHandshake); // will set the NULL handshake and throw SyncFailed("sync() Response error message"); // fail this sync attempt. } if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. // Update Handshake for(int x = 0, i = 0; i < ManglerKeyExpansionCount; i++) // For the required number of loops, x = ResponseGenerator.Encrypt(x); // have Mangler chase it's tail. PadBuffer NewHandshake; // Grab a new handshake buffer. for(unsigned int x = 0, i = 0; i < SNFHandshakeSize; i++) // Create the new handshake as a NewHandshake.push_back( // mangler hash of the current x = ResponseGenerator.Encrypt(x)); // key state (proper length of course). Handshake(NewHandshake); // Save our new handshake to disk. // Interpret Rulebase Response myLOGmgr->updateAvailableUTC(RulebaseResponse.snf_sync_rulebase_utc); // Store the latest update UTC. if(myLOGmgr->isUpdateAvailable()) { // If a new update is read then postUpdateTrigger(RulebaseResponse.snf_sync_rulebase_utc); // create an update trigger file. } // Write our Client reports (multi-line) CurrentThreadState(SYNC_Send_GBUdb_Alerts); string ClientReport; ClientReport.append("\n"); sendDataTimeout(SyncServer, SessionDog, ClientReport); ClientReport = ""; if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. // Insert our GBUdb Alerts. list Alerts; // Make a list of GBUdb Alerts. myGBUdbmgr->GetAlertsForSync(Alerts); // Get them from our GBUdb. list::iterator iA; for(iA = Alerts.begin(); iA != Alerts.end(); iA++) { // Convert each alert in our list ClientReport.append((*iA).toXML()); // into XML, follow it up ClientReport.append("\n"); // with a new line, and send it } sendDataTimeout(SyncServer, SessionDog, ClientReport); // Send the Client report data. ClientReport = ""; // Clear the buffer. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. // Send Status Reports - one line at a time. CurrentThreadState(SYNC_Send_Status_Reports); /** *** Instead of splitting up the reports by line we will try sending them *** all at once using the new sendDataTimeout() function. *** if(0 < ReportsBuffer.length()) { // If we have reports - send them. string DataToSend = getReports(); // Grab a copy and clear the buffer. int Cursor = 0; // We need a cursor and a length int Length = 0; // to help us feed this line by line. while(Cursor < DataToSend.length()) { // While we have more data... Length = DataToSend.find_first_of('\n', Cursor); // Find the end of the first line. if(string::npos == Length) break; // If we can't then we're done. Length = (Length + 1) - Cursor; // If we can, convert that to length. SyncServer.transmit( // Get and send the line using the DataToSend.substr(Cursor, Length).c_str(), // substring function. Length ); Cursor = Cursor + Length; // Move the cursor for the next line. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. } } **/ if(0 < ReportsBuffer.length()) { // If we have reports to send string DataToSend = getReports(); // get (and clear) the reports and sendDataTimeout(SyncServer, SessionDog, DataToSend); // send them (mindful of timeout). } if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. // Send Samples - one line at a time. CurrentThreadState(SYNC_Send_Samples); /*** if(0 < SamplesBuffer.length()) { string DataToSend = getSamples(); int Cursor = 0; // We need a cursor and a length int Length = 0; // to help us feed this line by line. while(Cursor < DataToSend.length()) { // While we have more data... Length = DataToSend.find_first_of('\n', Cursor); // Find the end of the first line. if(string::npos == Length) break; // If we can't then we're done. Length = (Length + 1) - Cursor; // If we can, convert that to length. SyncServer.transmit( // Get and send the line using the DataToSend.substr(Cursor, Length).c_str(), // substring function. Length ); Cursor = Cursor + Length; // Move the cursor for the next line. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. } } ***/ if(0 < SamplesBuffer.length()) { // If we have samples to send string DataToSend = getSamples(); // get (and clear) the samples and sendDataTimeout(SyncServer, SessionDog, DataToSend); // send them (mindful of timeout). } if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. // Terminate the client messages. CurrentThreadState(SYNC_Send_End_Of_Report); ClientReport.append("\n"); sendDataTimeout(SyncServer, SessionDog, ClientReport); // Send the Client report. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. // Read the Server response (multi-line) CurrentThreadState(SYNC_Read_Server_Response); string ServerResponse; string ResponseLine; while(string::npos == ResponseLine.find("\n")) { // Until we find the ending... ResponseLine = readLineTimeout(SyncServer, SessionDog); // Read a line. if(0 >= ResponseLine.length()) { // If we get an empty line throw SyncFailed("sync() server response empty line"); // then it's an error. } ServerResponse.append(ResponseLine); // Append the line. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time. } snf_sync ServerMessages( ServerResponse.c_str(), ServerResponse.length()); // Interpret what we read. if( // Check that it's good... ServerMessages.bad() // A complete packet was read. ) throw SyncFailed("sync() ServerMessages.bad()"); // If not then throw. // At this point we should have a good Server response. CurrentThreadState(SYNC_Close_Connection); SyncServer.close(); // Close the connection. evolvePad(Challenge.snf_sync_challenge_txt); // Use this event for more entropy. // Import any GBUdb reflections. CurrentThreadState(SYNC_Parse_GBUdb_Reflections); if(0 < ServerMessages.ServerGBUAlertHandler.AlertList.size()) { // If we have received reflections myGBUdbmgr->ProcessReflections( // then process them through our ServerMessages.ServerGBUAlertHandler.AlertList // GBUdb. ); } /*** On Sync Override set sync timer to override time. If no override **** then be sure to reset the timer to the current CFG value if it **** is not already there. Also, if sync override is not engaged then **** be sure the overrid flag is set to -1 indicating it is off. **** Configure() code assumes we are handling the override sync timer **** functions this way. ***/ // Assign the SyncSecsOverride with the value we retrieved. It will // either be a seconds value, or a -1 indicating it was absent from // the server message. SyncSecsOverride = ServerMessages.snf_sync_server_resync_secs; // What was the SyncOverride? const int SecsAsms = 1000; // Multiplier - seconds to milliseconds. if(0 > SyncSecsOverride) { // If the sync timer IS NOT in override, if(SYNCTimer.getDuration() != SecsAsMSecs(SyncSecsConfigured)) { // And the config time is different than SYNCTimer.setDuration(SyncSecsConfigured * SecsAsms); // the timer's current setting then set } // the timer to the new value. } else { // If the sync timer IS in override now, if(SYNCTimer.getDuration() != SecsAsMSecs(SyncSecsOverride)) { // and the override is different than the SYNCTimer.setDuration(SecsAsMSecs(SyncSecsOverride)); // current setting then override the setting } // with the new value. } // All done CurrentThreadState(SYNC_Log_Event); (*myLOGmgr).RecordSyncEvent(); // Finished that -- so log the event. } catch (exception& e) { // SYNC Failed and we know more. const int snf_UNKNOWN_ERROR = 99; // Report an error (unknown code) string ERROR_SYNC_FAILEDmsg = CurrentThreadState().Name; // Format a useful state message. ERROR_SYNC_FAILEDmsg.append(": "); ERROR_SYNC_FAILEDmsg.append(e.what()); (*myLOGmgr).logThisError( // Log the error (if possible) "SNF_NETWORK", snf_UNKNOWN_ERROR, ERROR_SYNC_FAILEDmsg ); } catch (...) { // SYNC Failed if we're here. const int snf_UNKNOWN_ERROR = 99; // Report an error (unknown code) string ERROR_SYNC_FAILEDmsg = CurrentThreadState().Name; // Format a useful state message. ERROR_SYNC_FAILEDmsg.append(": Panic!"); (*myLOGmgr).logThisError( // Log the error (if possible) "SNF_NETWORK", snf_UNKNOWN_ERROR, ERROR_SYNC_FAILEDmsg ); } } }