You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. // snfNETmgr.cpp
  2. //
  3. // (C) Copyright 2006 - 2020 ARM Research Labs, LLC
  4. // See www.armresearch.com for the copyright terms.
  5. //
  6. // See snfNETmgr.hpp for details.
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <ctime>
  10. #include <cstring>
  11. #include <string>
  12. #include <vector>
  13. #include <fstream>
  14. #include <sstream>
  15. #include "snfNETmgr.hpp"
  16. #include "snf_sync.hpp"
  17. #include "../CodeDweller/mangler.hpp"
  18. #include "../CodeDweller/base64codec.hpp"
  19. // #include "tcp_watchdog.hpp" No longer using TCPWatchdog -- see below _M
  20. namespace cd = codedweller;
  21. ///// utilities ////////////////////////////////////////////////////////////////
  22. const int MSecsInSecs = 1000; // Multiplier - seconds to milliseconds.
  23. unsigned long long int SecsAsMSecs(unsigned int Secs) {
  24. return (MSecsInSecs * Secs);
  25. }
  26. //// snfNETmgr /////////////////////////////////////////////////////////////////
  27. const cd::ThreadType snfNETmgr::Type("snfNETManager"); // The thread's type.
  28. const cd::ThreadState snfNETmgr::Sleeping("Sleeping"); // Taking a break.
  29. const cd::ThreadState snfNETmgr::SYNC_Connect("Connecting"); // Connecting to SYNC server.
  30. const cd::ThreadState snfNETmgr::SYNC_Read_Challenge("Reading challenge"); // Reading challenge.
  31. const cd::ThreadState snfNETmgr::SYNC_Compute_Response("Computing crypto"); // Computing crypto response.
  32. const cd::ThreadState snfNETmgr::SYNC_Send_Response("Sending crypto"); // Sending crypto response.
  33. const cd::ThreadState snfNETmgr::SYNC_Read_Availabilty("Reading Availability"); // Reading rulebase status.
  34. const cd::ThreadState snfNETmgr::SYNC_Send_GBUdb_Alerts("Sending GBUdb"); // Sending GBUdb alerts.
  35. const cd::ThreadState snfNETmgr::SYNC_Send_Status_Reports("Sending Status"); // Sending status reports.
  36. const cd::ThreadState snfNETmgr::SYNC_Send_Samples("Sending Samples"); // Sending message samples.
  37. const cd::ThreadState snfNETmgr::SYNC_Send_End_Of_Report("Sending End"); // Sending end of client data.
  38. const cd::ThreadState snfNETmgr::SYNC_Read_Server_Response("Reading Server"); // Reading server data.
  39. const cd::ThreadState snfNETmgr::SYNC_Close_Connection("Closing Connection"); // Closing connection.
  40. const cd::ThreadState snfNETmgr::SYNC_Parse_GBUdb_Reflections("Parsing GBUdb"); // Parsing GBUdb reflections.
  41. const cd::ThreadState snfNETmgr::SYNC_Log_Event("Logging SYNC"); // Logging SYNC event.
  42. snfNETmgr::snfNETmgr() : // Starting up the NETmgr
  43. Thread(snfNETmgr::Type, "NET Manager"), // Network manager and Name.
  44. myLOGmgr(NULL), // No LOGmgr yet.
  45. isTimeToStop(false), // Not time to stop yet.
  46. isConfigured(false), // Not configured yet.
  47. SYNCTimer(30000), // Sync every 30 secs by default.
  48. SyncSecsOverride(-1) { // Override is -1 (off) by default.
  49. run(); // Run the thread.
  50. }
  51. snfNETmgr::~snfNETmgr() { // On descruction, NETmgr must
  52. stop(); // Stop it's thread (if not already)
  53. myLOGmgr = NULL; // Clear out the LOGmgr hookup
  54. isConfigured = false; // and the configured flag.
  55. }
  56. void snfNETmgr::stop() { // The stop method...
  57. if(!isTimeToStop) { // only does it's work once:
  58. isTimeToStop = true; // tells it's thread to stop
  59. join(); // and waits for it to shut down.
  60. }
  61. }
  62. void snfNETmgr::myTask() { // Here's the thread task.
  63. cd::Sleeper WaitASecond(1000); // Heartbeat timer.
  64. while(false == isTimeToStop) { // Until it's time to stop,
  65. CurrentThreadState(Sleeping); // post our status,
  66. WaitASecond(); // pause for a second,
  67. if(isConfigured) { // then poll our tasks.
  68. // Do stuff here that requires configuration data.
  69. if(SYNCTimer.isExpired()) { sync(); SYNCTimer.restart(); } // If it's time to sync - do it :-)
  70. }
  71. }
  72. }
  73. void snfNETmgr::linkLOGmgr(snfLOGmgr& L) { // Set the LOGmgr.
  74. myLOGmgr = &L;
  75. }
  76. void snfNETmgr::linkGBUdbmgr(snfGBUdbmgr& G) { // Set the GBUdbmgr.
  77. myGBUdbmgr = &G;
  78. }
  79. // In theory, configure will get called each time the rulebase manager loads
  80. // a new configuration / rulebase. The configure() method updates the bits of
  81. // NETmgr that run background tasks. Live-Data tasks pass their grab()bed
  82. // CFGData object in order to maintain self-consistency.
  83. void snfNETmgr::configure(snfCFGData& CFGData) { // Update the configuration.
  84. cd::ScopeMutex CFGDataExchange(ConfigMutex); // Lock the config data during updates.
  85. // Update the internal config data from CFGData while we are locked.
  86. // Internal functions which depend on this data will lock the object,
  87. // grab the bits they depend upon for that pass, and then unlock.
  88. RulebaseFilePath = CFGData.RuleFilePath; // Where we can find our rulebase?
  89. SyncHostName = CFGData.network_sync_host; // Where do we connect to sync?
  90. SyncHostPort = CFGData.network_sync_port; // What port do we use to sync?
  91. HandshakeFilePath = CFGData.paths_workspace_path + ".handshake"; // Where we store our handshake.
  92. UpdateReadyFilePath = CFGData.paths_workspace_path + "UpdateReady.txt"; // Where we put update trigger files.
  93. SyncSecsConfigured = CFGData.network_sync_secs; // Capture the configured sync time.
  94. if(0 > SyncSecsOverride) { // If the sync timer isn't in override,
  95. if(SYNCTimer.getDuration() != SecsAsMSecs(SyncSecsConfigured)) { // And the config time is different than
  96. SYNCTimer.setDuration(SecsAsMSecs(SyncSecsConfigured)); // the timer's current setting then set
  97. } // the timer to the new value.
  98. } // If we are in override, timer is set.
  99. License = CFGData.node_licenseid; // Capture our node id (license id).
  100. SecurityKey = CFGData.SecurityKey; // Capture our security key.
  101. evolvePad(CFGData.SecurityKey); // Seed our Pad generator with it.
  102. // Safety check before turning this on ;-)
  103. if(
  104. NULL != myLOGmgr &&
  105. NULL != myGBUdbmgr
  106. ) { // If we are properly linked then
  107. isConfigured = true; // at this point we are configured!
  108. }
  109. }
  110. void snfNETmgr::sendSample( // Send a sampled message...
  111. snfCFGData& CFGData, // Use this configuration,
  112. snfScanData& ScanData, // Include this scan data,
  113. const unsigned char* MessageBuffer, // This is the message itself
  114. int MessageLength // and it is this size.
  115. ) {
  116. std::string TimeStamp; (*myLOGmgr).Timestamp(TimeStamp); // Grab a timestamp.
  117. std::ostringstream XML; // Make formatting easier with this.
  118. //-- <sample...>
  119. XML << "<sample node=\'" << CFGData.node_licenseid << "\' "
  120. << "time=\'" << TimeStamp << "\' "
  121. << "result=\'" << ScanData.CompositeFinalResult << "\'>" << std::endl;
  122. //-- <ip...>
  123. XML << "<ip range=\'";
  124. std::string IPRange;
  125. switch(ScanData.SourceIPRange()) {
  126. case snfIPRange::Unknown: { IPRange = "Unknown"; break; } // Unknown - not defined.
  127. case snfIPRange::White: { IPRange = "White"; break; } // This is a good guy.
  128. case snfIPRange::Normal: { IPRange = "Normal"; break; } // Benefit of the doubt.
  129. case snfIPRange::New: { IPRange = "New"; break; } // It is new to us.
  130. case snfIPRange::Caution: { IPRange = "Caution"; break; } // This is suspicious.
  131. case snfIPRange::Black: { IPRange = "Black"; break; } // This is bad.
  132. case snfIPRange::Truncate: { IPRange = "Truncate"; break; } // Don't even bother looking.
  133. }
  134. cd::SocketAddress IP;
  135. IP.setAddress(ScanData.SourceIPRecord().IP);
  136. XML << IPRange << "\' ip=\'" << (std::string) cd::IP4Address(IP.getAddress()) << "\' t=\'";
  137. std::string IPType;
  138. switch(ScanData.SourceIPRecord().GBUdbData.Flag()) {
  139. case Good: { IPType = "Good"; break; }
  140. case Bad: { IPType = "Bad"; break; }
  141. case Ugly: { IPType = "Ugly"; break; }
  142. case Ignore: { IPType = "Ignore"; break; }
  143. }
  144. XML << IPType << "\' b=\'" << ScanData.SourceIPRecord().GBUdbData.Bad()
  145. << "\' g=\'" << ScanData.SourceIPRecord().GBUdbData.Good()
  146. << "\'/>" << std::endl;
  147. //-- <match...> as many as needed
  148. if(0 < ScanData.MatchRecords.size()) { // If we have match records - emit them.
  149. std::list<snf_match>::iterator iM; // Grab an iterator.
  150. for( // Emit each snf_match entry.
  151. iM = ScanData.MatchRecords.begin();
  152. iM != ScanData.MatchRecords.end();
  153. iM++) {
  154. XML << "<match r=\'" << (*iM).ruleid << "\' "
  155. << "g=\'" << (*iM).symbol << "\' "
  156. << "i=\'" << (*iM).index << "\' "
  157. << "e=\'" << (*iM).endex << "\' "
  158. << "f=\'" << (*iM).flag << "\'/>";
  159. }
  160. }
  161. //-- <msg...>
  162. XML << "<msg size=\'" << ScanData.ScanSize << "'>" << std::endl; // Starting with the msg element.
  163. cd::to_base64 EncodedMessageData(
  164. reinterpret_cast<const char*>(MessageBuffer), MessageLength); // Encode the message to base64.
  165. const int SampleLineLength = 64; // 64 bytes per line is good.
  166. for(int i = 0; i < MessageLength;) { // Now we break it into lines
  167. for(int l = 0; l < SampleLineLength && i < MessageLength; l++, i++) { // that are a reasonable length.
  168. XML << EncodedMessageData.at(i); // Emit one character at a time...
  169. } // At the end of a reasonable
  170. XML << std::endl; // length we terminate the line.
  171. }
  172. XML << "</msg>" << std::endl; // End of the <msg> element.
  173. //-- done with the sample!
  174. XML << "</sample>" << std::endl;
  175. // Last thing we do is post the formatted string to the buffer.
  176. const unsigned int SampleSafetyLimit = 100000; // 100 Kbyte limit on samples.
  177. cd::ScopeMutex DoNotDisturb(myMutex); // Don't bug me man I'm busy.
  178. if(SampleSafetyLimit < SamplesBuffer.length()) // If the samples buffer is full
  179. SamplesBuffer.clear(); // clear it before adding more.
  180. SamplesBuffer.append(XML.str()); // Append the XML to the buffer.
  181. }
  182. std::string snfNETmgr::getSamples() { // Synchronized way to get Samples.
  183. cd::ScopeMutex DoNotDisturb(myMutex); // Lock the mutex to protect our work.
  184. std::string SamplesBatch = SamplesBuffer; // Copy the samples to a new string.
  185. SamplesBuffer.clear(); // Clear the samples buffer.
  186. return SamplesBatch; // Return a batch of Samples.
  187. }
  188. void snfNETmgr::sendReport(const std::string& S) { // How to send a status report.
  189. const unsigned int ReportSafetyLimit = 100000; // 100 Kbytes limit on reports.
  190. cd::ScopeMutex DoNotDisturb(myMutex); // Lock the mutex for a moment.
  191. if(ReportSafetyLimit < ReportsBuffer.length()) // If the reports buffer is full
  192. ReportsBuffer.clear(); // clear it before adding more.
  193. ReportsBuffer.append(S); // Append the report.
  194. }
  195. std::string snfNETmgr::getReports() { // Synchronized way to get Reports.
  196. cd::ScopeMutex DoNotDisturb(myMutex); // Lock the mutex to protect our work.
  197. std::string ReportsBatch = ReportsBuffer; // Copy the reports to a new string.
  198. ReportsBuffer.clear(); // Clear the reports buffer.
  199. return ReportsBatch; // Return a batch of Reports.
  200. }
  201. cd::RuntimeCheck RulebaseUTCGoodTimestampLength("SNFNetmgr.cpp:RulebaseUTC snprintf(...) == CorrectTimestampLength");
  202. std::string& snfNETmgr::RulebaseUTC(std::string& t) { // Gets local rulebase file UTC.
  203. struct stat RulebaseStat; // First we need a stat buffer.
  204. if(0 != stat(RulebaseFilePath.c_str(), &RulebaseStat)) { // If we can't get the stat we
  205. t.append("000000000000"); return t; // will return 000000000000 to
  206. } // make sure we should get the file.
  207. struct tm RulebaseTime; // Allocate a time structure.
  208. RulebaseTime = *(gmtime(&RulebaseStat.st_mtime)); // Copy the file time to it as UTC.
  209. char TimestampBfr[16]; // Timestamp buffer.
  210. size_t l = snprintf( // Format yyyymmddhhmmss
  211. TimestampBfr, sizeof(TimestampBfr),
  212. "%04d%02d%02d%02d%02d%02d",
  213. RulebaseTime.tm_year+1900,
  214. RulebaseTime.tm_mon+1,
  215. RulebaseTime.tm_mday,
  216. RulebaseTime.tm_hour,
  217. RulebaseTime.tm_min,
  218. RulebaseTime.tm_sec
  219. );
  220. const size_t CorrectTimestampLength = 4+2+2+2+2+2;
  221. RulebaseUTCGoodTimestampLength(l == CorrectTimestampLength);
  222. t.append(TimestampBfr); // Append the timestamp to t
  223. return t; // and return it to the caller.
  224. }
  225. unsigned long snfNETmgr::ResolveHostIPFromName(const std::string& N) { // Host name resolution tool.
  226. cd::ScopeMutex OneAtATimePlease(ResolverMutex); // Resolve only one at a time.
  227. unsigned long IP = inet_addr(N.c_str()); // See if it's an IP.
  228. if (INADDR_NONE == IP) { // If it's not an IP resolve it.
  229. hostent* H = gethostbyname(N.c_str()); // Resolve the host.
  230. if (NULL == H) { // If we didn't get a resolution
  231. return INADDR_NONE; // return no address.
  232. } // If we did resolve the address
  233. IP = *((unsigned long*)H->h_addr_list[0]); // get the primary entry.
  234. }
  235. return ntohl(IP); // Return what we got (host order)
  236. }
  237. // The Evolving One Time Pad engine is just slightly better than calling
  238. // rand() with the system time as a seed. However, it does have the advantage
  239. // that in order to guess it's initial state an attacker would need to already
  240. // know the license id and authentication. It also has the advantage that it
  241. // adds small amounts of entropy over time and never really forgets them. For
  242. // example, the exact time between calls to evolvePad is dependent on how long
  243. // it takes to sync which is dependent on how much data there is to report
  244. // which is dependent on the number and size of messages scanned etc... and
  245. // this is also impacted a bit by network performance issues during the sync.
  246. // Sensitivity to this entropy has millisecond resolution. This is a cross-
  247. // platform solution that depends only on our own code ;-)
  248. void snfNETmgr::evolvePad(std::string Entropy) { // Add entropy and evolve.
  249. cd::ScopeMutex OneAtATimePlease(PadMutex); // Protect the one time pad.
  250. myLOGmgr->Timestamp(Entropy); // Time matters ;-)
  251. for(unsigned int a = 0; a < Entropy.length(); a++) { // Add the entropy to our generator.
  252. PadGenerator.Encrypt(Entropy.at(a));
  253. }
  254. cd::msclock rt = myLOGmgr->RunningTime(); // Get the elapsed running time so far.
  255. unsigned char* rtb = reinterpret_cast<unsigned char*>(&rt); // Convert that long long into bytes.
  256. for(unsigned int a = 0; a < sizeof(cd::msclock); a++) { // Encrypt those bytes one by one
  257. PadGenerator.Encrypt(rtb[a]); // to add more entropy.
  258. }
  259. }
  260. // To get a pad of any length you like, use the OneTimePad()
  261. // Note that we don't assign a value to x before using it! If we get lucky,
  262. // we will get some random value from ram as additional entropy ;-) If we end
  263. // up starting with zero, that's ok too.
  264. PadBuffer snfNETmgr::OneTimePad(int Len) { // Get Len bytes of one time pad.
  265. PadBuffer B; // Start with a buffer.
  266. B.reserve(Len); // Reserve Len bytes.
  267. unsigned char x = PadGenerator.Encrypt(0); // Get an unexposed byte to start with.
  268. for(int a = 0; a < Len; a++) { // Create Len bytes of pad by evolving
  269. B.push_back(x = PadGenerator.Encrypt(x)); // x through itself and copying the
  270. } // data into the buffer.
  271. return B; // Return the result.
  272. }
  273. // Handshake tries to return the current stored handshake. If it can't then it
  274. // returns a new handshake based on data from the pad generator.
  275. PadBuffer snfNETmgr::Handshake() { // What is the current handshake?
  276. if(CurrentHandshake.size() != SNFHandshakeSize) { // If we don't have one make one!
  277. CurrentHandshake = OneTimePad(SNFHandshakeSize); // Set up a default handshake to use
  278. try { // if we can't remember the real one.
  279. std::ifstream HSF(HandshakeFilePath.c_str(), std::ios::binary); // Open the handshake file.
  280. char* bfr = reinterpret_cast<char*>(&CurrentHandshake[0]); // Manufacture a proper pointer.
  281. HSF.read(bfr, SNFHandshakeSize); // Read the data (overwrite the HSB).
  282. HSF.close(); // Close the file.
  283. } catch(...) { } // Ignore any errors.
  284. }
  285. return CurrentHandshake; // Return the buffer.
  286. }
  287. PadBuffer& snfNETmgr::Handshake(PadBuffer& NewHandshake) { // Store a new handshake.
  288. CurrentHandshake = NewHandshake; // Grab the new handshake
  289. try { // then try to store it...
  290. std::ofstream HSF(
  291. HandshakeFilePath.c_str(), std::ios::binary | std::ios::trunc); // Open the handshake file.
  292. char* bfr = reinterpret_cast<char*>(&NewHandshake[0]); // Access the raw buffer.
  293. HSF.write(bfr, NewHandshake.size()); // Replace the old handshake
  294. HSF.close(); // close the file.
  295. } catch(...) {} // Ignore errors.
  296. return NewHandshake; // Return what we were given.
  297. }
  298. void snfNETmgr::postUpdateTrigger(std::string& updateUTC) { // Post an update trigger file.
  299. try { // Safely post an update trigger.
  300. std::ofstream HSF(
  301. UpdateReadyFilePath.c_str(), std::ios::binary | std::ios::trunc); // Open/create the trigger file.
  302. char* bfr = reinterpret_cast<char*>(&updateUTC[0]); // Access the raw UTC buffer.
  303. HSF.write(bfr, updateUTC.size()); // Write the update timestamp.
  304. HSF.close(); // close the file.
  305. } catch(...) {} // Ignore errors.
  306. }
  307. // Utility to read a line from a non-blocking TCPHost & check the timeout.
  308. const unsigned int MaxReadLineLength = 1024; // How long a line can be.
  309. std::string readLineTimeout(cd::TCPHost& S, cd::Timeout& T) { // Read a line from S until T.
  310. cd::Sleeper WaitForMoreData(50); // How long to wait when no data.
  311. std::string LineBuffer = ""; // Buffer for the line.
  312. while( // Keep going as long as:
  313. false == T.isExpired() && // our timeout has not expired AND
  314. MaxReadLineLength > LineBuffer.length() // we haven't reached our limit.
  315. ) {
  316. char c = 0; // One byte at a time
  317. if(1 == S.receive(&c, sizeof(c))) { // Read from the TCPHost.
  318. LineBuffer.push_back(c); // Push the byte onto the string.
  319. if('\n' == c) break; // If it was a newline we're done!
  320. } else { // If we didn't get any data
  321. WaitForMoreData(); // pause before our next run.
  322. }
  323. }
  324. return LineBuffer; // Always return our buffer.
  325. }
  326. // Utility to write data to a non-blocking TCPHost & check the timeout.
  327. // Some networks can only handle small packets and fragmentation can be a
  328. // problem. Also, on Win* especially, sending small chunks is _MUCH_ more
  329. // reliable than trying to send large buffers all at once. SO - here we break
  330. // down our sending operations into medium sized chunks of data. The underlying
  331. // os can reorganize these chunks as needed for the outgouing stream. If the OS
  332. // needs us to slow down (doesn't send full chunks) then we introduce a small
  333. // delay between chunks to give the channel more time.
  334. const int MaxSendChunkSize = 512; // Size of one chunk in a write.
  335. void sendDataTimeout(cd::TCPHost& S, cd::Timeout& T, char* Bfr, int Len) { // Send and keep track of time.
  336. cd::Sleeper WaitForMoreRoom(15); // Wait to send more data.
  337. int Remaining = Len; // This is how much we have left.
  338. while( // For as long as:
  339. false == T.isExpired() && // We still have time left AND
  340. 0 < Remaining // We still have data left
  341. ) {
  342. int ThisChunkSize = Remaining; // Hope to send it all in one chunk
  343. if(MaxSendChunkSize < ThisChunkSize) ThisChunkSize = MaxSendChunkSize; // but break it down as needed.
  344. int SentThisTime = S.transmit(Bfr, ThisChunkSize); // Send the data. How much went?
  345. Remaining -= SentThisTime; // Calculate how much is left.
  346. Bfr += SentThisTime; // Move our pointer (old school!)
  347. if(ThisChunkSize > SentThisTime) WaitForMoreRoom(); // If some of this chunk didn't go
  348. } // the pause before the next chunk.
  349. }
  350. void sendDataTimeout(cd::TCPHost& S, cd::Timeout& T, std::string& D) { // Send a string and keep track
  351. sendDataTimeout(S, T, const_cast<char*>(D.c_str()), D.length()); // of time. (Polymorphism is fun)
  352. }
  353. void snfNETmgr::sync() { // Synchronize with central command.
  354. // Keep these things in scope. This is how we roll.
  355. std::string HostName;
  356. int HostPort;
  357. std::string Secret;
  358. std::string Node;
  359. // Grab our configuration data (marchng orders).
  360. if(!isConfigured) return; // If we're not configured, don't!
  361. else {
  362. cd::ScopeMutex GettingConfig(ConfigMutex); // Temporarily lock our config.
  363. HostName = SyncHostName; // We will connect to this host.
  364. HostPort = SyncHostPort; // We will connect to this port.
  365. Secret = SecurityKey; // Get the security key.
  366. Node = License; // Get the Node ID.
  367. }
  368. try { // Lots can go wrong so catch it :-)
  369. // 20080326 _M Blocking sockets tend to lock up so I've refactored this
  370. // code to use non-blocking sockets. This is actually part of the previous
  371. // refactor (TCPWatchdog see below) since without the watchdog there is no
  372. // way to get out of a blocking socket if it's dead.
  373. // 20080325 _M TCPWatchdog is a brute. It doesn't pay attention to thread
  374. // states. A weird bug showed up where the SYNC session seemed to hang and
  375. // the TCPWatchdog was left alive. In the process of hunting down this bug
  376. // I decided to remove the TCPWatchdog and put appropriate timeout checking
  377. // in each of the comms loops instead. So, from now on:
  378. // if(SessionDog.isExpired()) throw SyncFailed("Out Of Time");
  379. const int SyncSessionTimeout = 2 * SYNCTimer.getDuration(); // Timeout is twice poll time.
  380. cd::Timeout SessionDog(SyncSessionTimeout); // Give this long for a session.
  381. // Connect to the sync host.
  382. CurrentThreadState(SYNC_Connect);
  383. cd::SocketAddress SyncHostAddress; // We'll need an address.
  384. SyncHostAddress.setPort(HostPort); // Set the port.
  385. SyncHostAddress.setAddress(ResolveHostIPFromName(HostName)); // Resolve and set the IP.
  386. cd::TCPHost SyncServer(SyncHostAddress); // Set up a host connection.
  387. SyncServer.makeNonBlocking(); // Make the connection non-blocking.
  388. cd::PollTimer WaitForOpen(10, 340); // Expand 10ms to 340ms between tries.
  389. while(!SessionDog.isExpired()) { // Wait & Watch for a good connection.
  390. try { SyncServer.open(); } // Try opening the connection.
  391. catch(const std::exception& e) { // If we get an exception then
  392. std::string ConnectFailMessage = "snfNETmgr::sync().open() "; // format a useful message about
  393. ConnectFailMessage.append(e.what()); // the error and then throw
  394. throw SyncFailed(ConnectFailMessage); // a SyncFailed exception.
  395. }
  396. if(SyncServer.isOpen()) break; // When successful, let's Go!
  397. else WaitForOpen.pause(); // When not yet successful, pause
  398. } // then try again if we have time.
  399. if(!SyncServer.isOpen()) throw SyncFailed("Connect Timed Out"); // Check our connection.
  400. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  401. // Start communicating.
  402. std::string LineBuffer = ""; // Input Line Buffer.
  403. // Read challenge
  404. CurrentThreadState(SYNC_Read_Challenge);
  405. LineBuffer = readLineTimeout(SyncServer, SessionDog); // Read the challenge line.
  406. snf_sync Challenge(LineBuffer.c_str(), LineBuffer.length()); // Interpret what we read.
  407. if( // Check that it's good...
  408. Challenge.bad() || // A complete packet was read
  409. 0 >= Challenge.snf_sync_challenge_txt.length() // and the challenge is present.
  410. ) throw SyncFailed("sync() Challenge.bad()"); // If not then throw.
  411. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  412. // Write response
  413. CurrentThreadState(SYNC_Compute_Response);
  414. cd::from_base64 DecodedChallenge(Challenge.snf_sync_challenge_txt); // Decode the challenge.
  415. //--- Prepare the secret.
  416. cd::Mangler ResponseGenerator; // Grab a mangler.
  417. for(unsigned int i = 0; i < Secret.length(); i++) // Fill it with the
  418. ResponseGenerator.Encrypt(Secret.at(i)); // security key.
  419. const int ManglerKeyExpansionCount = 1024; // Loop this many to randomize.
  420. for(int x = 0, i = 0; i < ManglerKeyExpansionCount; i++) // For the required number of loops,
  421. x = ResponseGenerator.Encrypt(x); // have Mangler chase it's tail.
  422. //--- Absorb the challenge.
  423. for(unsigned int i = 0; i < DecodedChallenge.size(); i++) // Evolve through the challenge.
  424. ResponseGenerator.Encrypt(DecodedChallenge.at(i));
  425. /*** We now have half of the key for this session ***/
  426. //--- Encrypt our Pad.
  427. PadBuffer NewPad = OneTimePad(); // Grab a new Pad (default size).
  428. cd::base64buffer ResponseBin; // With the key now established,
  429. for(unsigned int i = 0; i < NewPad.size(); i++) // encrypt the one time pad for
  430. ResponseBin.push_back( // transfer.
  431. ResponseGenerator.Encrypt(NewPad[i]));
  432. //--- Encrypt our Handshake.
  433. PadBuffer CurrentHandshake = Handshake(); // Recall the secret handshake.
  434. for(unsigned int i = 0; i < CurrentHandshake.size(); i++) // Encrypt that into the stream.
  435. ResponseBin.push_back(
  436. ResponseGenerator.Encrypt(CurrentHandshake[i]));
  437. //--- Encrypt our Signature.
  438. for(unsigned int x = 0, i = 0; i < SNFSignatureSize; i++) // Generate a hash by having Mangler
  439. ResponseBin.push_back( // chase it's tail for the appropriate
  440. x = ResponseGenerator.Encrypt(x)); // number of bytes.
  441. //--- Encode our response as base64 and send it.
  442. cd::to_base64 ResponseTxt(ResponseBin); // Encode the cyphertext as base64.
  443. std::string ResponseTxtString; // Create a handy string and place
  444. ResponseTxtString.assign(ResponseTxt.begin(), ResponseTxt.end()); // the base 64 text into it.
  445. std::string ResponseMsg; // Build an appropriate response
  446. ResponseMsg.append("<snf><sync><response nodeid=\'"); // identifying this node
  447. ResponseMsg.append(Node); // with the license id
  448. ResponseMsg.append("\' text=\'"); // and providing an appropriately
  449. ResponseMsg.append(ResponseTxtString); // mangled response string
  450. ResponseMsg.append("\'/></sync></snf>\n"); // for authentication.
  451. CurrentThreadState(SYNC_Send_Response);
  452. sendDataTimeout(SyncServer, SessionDog, ResponseMsg); // Send the response.
  453. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  454. // Read rulebase info or error
  455. CurrentThreadState(SYNC_Read_Availabilty);
  456. LineBuffer = readLineTimeout(SyncServer, SessionDog); // Read the rulebase status line.
  457. snf_sync RulebaseResponse(LineBuffer.c_str(), LineBuffer.length()); // Interpret what we read.
  458. if( // Check that it's good...
  459. RulebaseResponse.bad() // A complete packet was read.
  460. ) throw SyncFailed("sync() Response.bad()"); // If not then throw.
  461. if(0 < RulebaseResponse.snf_sync_error_message.length()) { // If the response was an error
  462. PadBuffer NewNullHandshake; // then we will assume we are out
  463. NewNullHandshake.assign(SNFHandshakeSize, 0); // of sync with the server so we
  464. Handshake(NewNullHandshake); // will set the NULL handshake and
  465. throw SyncFailed("sync() Response error message"); // fail this sync attempt.
  466. }
  467. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  468. // Update Handshake
  469. for(int x = 0, i = 0; i < ManglerKeyExpansionCount; i++) // For the required number of loops,
  470. x = ResponseGenerator.Encrypt(x); // have Mangler chase it's tail.
  471. PadBuffer NewHandshake; // Grab a new handshake buffer.
  472. for(unsigned int x = 0, i = 0; i < SNFHandshakeSize; i++) // Create the new handshake as a
  473. NewHandshake.push_back( // mangler hash of the current
  474. x = ResponseGenerator.Encrypt(x)); // key state (proper length of course).
  475. Handshake(NewHandshake); // Save our new handshake to disk.
  476. // Interpret Rulebase Response
  477. myLOGmgr->updateAvailableUTC(RulebaseResponse.snf_sync_rulebase_utc); // Store the latest update UTC.
  478. if(myLOGmgr->isUpdateAvailable()) { // If a new update is read then
  479. postUpdateTrigger(RulebaseResponse.snf_sync_rulebase_utc); // create an update trigger file.
  480. }
  481. // Write our Client reports (multi-line)
  482. CurrentThreadState(SYNC_Send_GBUdb_Alerts);
  483. std::string ClientReport;
  484. ClientReport.append("<snf><sync><client>\n");
  485. sendDataTimeout(SyncServer, SessionDog, ClientReport);
  486. ClientReport = "";
  487. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  488. // Insert our GBUdb Alerts.
  489. std::list<GBUdbAlert> Alerts; // Make a list of GBUdb Alerts.
  490. myGBUdbmgr->GetAlertsForSync(Alerts); // Get them from our GBUdb.
  491. std::list<GBUdbAlert>::iterator iA;
  492. for(iA = Alerts.begin(); iA != Alerts.end(); iA++) { // Convert each alert in our list
  493. ClientReport.append((*iA).toXML()); // into XML, follow it up
  494. ClientReport.append("\n"); // with a new line, and send it
  495. }
  496. sendDataTimeout(SyncServer, SessionDog, ClientReport); // Send the Client report data.
  497. ClientReport = ""; // Clear the buffer.
  498. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  499. // Send Status Reports - one line at a time.
  500. CurrentThreadState(SYNC_Send_Status_Reports);
  501. /**
  502. *** Instead of splitting up the reports by line we will try sending them
  503. *** all at once using the new sendDataTimeout() function.
  504. ***
  505. if(0 < ReportsBuffer.length()) { // If we have reports - send them.
  506. string DataToSend = getReports(); // Grab a copy and clear the buffer.
  507. int Cursor = 0; // We need a cursor and a length
  508. int Length = 0; // to help us feed this line by line.
  509. while(Cursor < DataToSend.length()) { // While we have more data...
  510. Length = DataToSend.find_first_of('\n', Cursor); // Find the end of the first line.
  511. if(string::npos == Length) break; // If we can't then we're done.
  512. Length = (Length + 1) - Cursor; // If we can, convert that to length.
  513. SyncServer.transmit( // Get and send the line using the
  514. DataToSend.substr(Cursor, Length).c_str(), // substring function.
  515. Length
  516. );
  517. Cursor = Cursor + Length; // Move the cursor for the next line.
  518. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  519. }
  520. }
  521. **/
  522. if(0 < ReportsBuffer.length()) { // If we have reports to send
  523. std::string DataToSend = getReports(); // get (and clear) the reports and
  524. sendDataTimeout(SyncServer, SessionDog, DataToSend); // send them (mindful of timeout).
  525. }
  526. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  527. // Send Samples - one line at a time.
  528. CurrentThreadState(SYNC_Send_Samples);
  529. /***
  530. if(0 < SamplesBuffer.length()) {
  531. string DataToSend = getSamples();
  532. int Cursor = 0; // We need a cursor and a length
  533. int Length = 0; // to help us feed this line by line.
  534. while(Cursor < DataToSend.length()) { // While we have more data...
  535. Length = DataToSend.find_first_of('\n', Cursor); // Find the end of the first line.
  536. if(string::npos == Length) break; // If we can't then we're done.
  537. Length = (Length + 1) - Cursor; // If we can, convert that to length.
  538. SyncServer.transmit( // Get and send the line using the
  539. DataToSend.substr(Cursor, Length).c_str(), // substring function.
  540. Length
  541. );
  542. Cursor = Cursor + Length; // Move the cursor for the next line.
  543. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  544. }
  545. }
  546. ***/
  547. if(0 < SamplesBuffer.length()) { // If we have samples to send
  548. std::string DataToSend = getSamples(); // get (and clear) the samples and
  549. sendDataTimeout(SyncServer, SessionDog, DataToSend); // send them (mindful of timeout).
  550. }
  551. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  552. // Terminate the client messages.
  553. CurrentThreadState(SYNC_Send_End_Of_Report);
  554. ClientReport.append("</client></sync></snf>\n");
  555. sendDataTimeout(SyncServer, SessionDog, ClientReport); // Send the Client report.
  556. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  557. // Read the Server response (multi-line)
  558. CurrentThreadState(SYNC_Read_Server_Response);
  559. std::string ServerResponse;
  560. std::string ResponseLine;
  561. while(std::string::npos == ResponseLine.find("</snf>\n")) { // Until we find the ending...
  562. ResponseLine = readLineTimeout(SyncServer, SessionDog); // Read a line.
  563. if(0 >= ResponseLine.length()) { // If we get an empty line
  564. throw SyncFailed("sync() server response empty line"); // then it's an error.
  565. }
  566. ServerResponse.append(ResponseLine); // Append the line.
  567. if(SessionDog.isExpired()) throw SyncFailed("Out Of Time"); // Check our session time.
  568. }
  569. snf_sync ServerMessages(
  570. ServerResponse.c_str(), ServerResponse.length()); // Interpret what we read.
  571. if( // Check that it's good...
  572. ServerMessages.bad() // A complete packet was read.
  573. ) throw SyncFailed("sync() ServerMessages.bad()"); // If not then throw.
  574. // At this point we should have a good Server response.
  575. CurrentThreadState(SYNC_Close_Connection);
  576. SyncServer.close(); // Close the connection.
  577. evolvePad(Challenge.snf_sync_challenge_txt); // Use this event for more entropy.
  578. // Import any GBUdb reflections.
  579. CurrentThreadState(SYNC_Parse_GBUdb_Reflections);
  580. if(0 < ServerMessages.ServerGBUAlertHandler.AlertList.size()) { // If we have received reflections
  581. myGBUdbmgr->ProcessReflections( // then process them through our
  582. ServerMessages.ServerGBUAlertHandler.AlertList // GBUdb.
  583. );
  584. }
  585. /*** On Sync Override set sync timer to override time. If no override
  586. **** then be sure to reset the timer to the current CFG value if it
  587. **** is not already there. Also, if sync override is not engaged then
  588. **** be sure the overrid flag is set to -1 indicating it is off.
  589. **** Configure() code assumes we are handling the override sync timer
  590. **** functions this way.
  591. ***/
  592. // Assign the SyncSecsOverride with the value we retrieved. It will
  593. // either be a seconds value, or a -1 indicating it was absent from
  594. // the server message.
  595. SyncSecsOverride = ServerMessages.snf_sync_server_resync_secs; // What was the SyncOverride?
  596. const int SecsAsms = 1000; // Multiplier - seconds to milliseconds.
  597. if(0 > SyncSecsOverride) { // If the sync timer IS NOT in override,
  598. if(SYNCTimer.getDuration() != SecsAsMSecs(SyncSecsConfigured)) { // And the config time is different than
  599. SYNCTimer.setDuration(SyncSecsConfigured * SecsAsms); // the timer's current setting then set
  600. } // the timer to the new value.
  601. } else { // If the sync timer IS in override now,
  602. if(SYNCTimer.getDuration() != SecsAsMSecs(SyncSecsOverride)) { // and the override is different than the
  603. SYNCTimer.setDuration(SecsAsMSecs(SyncSecsOverride)); // current setting then override the setting
  604. } // with the new value.
  605. }
  606. // All done
  607. CurrentThreadState(SYNC_Log_Event);
  608. (*myLOGmgr).RecordSyncEvent(); // Finished that -- so log the event.
  609. }
  610. catch (const std::exception& e) { // SYNC Failed and we know more.
  611. const int snf_UNKNOWN_ERROR = 99; // Report an error (unknown code)
  612. std::string ERROR_SYNC_FAILEDmsg = CurrentThreadState().Name; // Format a useful state message.
  613. ERROR_SYNC_FAILEDmsg.append(": ");
  614. ERROR_SYNC_FAILEDmsg.append(e.what());
  615. (*myLOGmgr).logThisError( // Log the error (if possible)
  616. "SNF_NETWORK", snf_UNKNOWN_ERROR, ERROR_SYNC_FAILEDmsg
  617. );
  618. }
  619. catch (...) { // SYNC Failed if we're here.
  620. const int snf_UNKNOWN_ERROR = 99; // Report an error (unknown code)
  621. std::string ERROR_SYNC_FAILEDmsg = CurrentThreadState().Name; // Format a useful state message.
  622. ERROR_SYNC_FAILEDmsg.append(": Panic!");
  623. (*myLOGmgr).logThisError( // Log the error (if possible)
  624. "SNF_NETWORK", snf_UNKNOWN_ERROR, ERROR_SYNC_FAILEDmsg
  625. );
  626. }
  627. }