1 /* $Id: ncbi_monkey.cpp 590725 2019-08-03 02:06:11Z lavr $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Dmitriy Elisov
27 *
28 * File Description:
29 * Chaos Monkey - a library that is hooked up with ncbi_socket.c to simulate
30 * problems in network connectivity - losses, bad data, delays.
31 *
32 */
33
34 #include <ncbi_pch.hpp>
35
36 #include "ncbi_priv.h"
37
38 #ifdef NCBI_MONKEY
39 # include <connect/ncbi_connutil.h>
40 # include <corelib/ncbi_system.hpp>
41 # include <corelib/ncbireg.hpp>
42 # include <corelib/ncbithr.hpp>
43 # include <corelib/env_reg.hpp>
44 # include <corelib/ncbiapp.hpp>
45 # include <corelib/ncbimisc.hpp>
46 # include "ncbi_monkeyp.hpp"
47 # include <list>
48 # include <vector>
49
50 /* OS-dependent way to set socket errors */
51 # ifdef NCBI_OS_MSWIN
52 # define MONKEY_SET_SOCKET_ERROR(error) WSASetLastError(error)
53 # define MONKEY_ENOPROTOOPT WSAENOPROTOOPT
54 # else
55 # define MONKEY_SET_SOCKET_ERROR(error) errno = error
56 # define MONKEY_ENOPROTOOPT ENOPROTOOPT
57 # endif /* NCBI_OS_MSWIN */
58
59 /* Include OS-specific headers */
60 # ifdef NCBI_OS_MSWIN
61 # include <regex>
62 # else
63 # include <arpa/inet.h>
64 # include <sys/types.h>
65 # include <sys/socket.h>
66 # include <sys/un.h>
67 # endif /* NCBI_OS_MSWIN */
68
69
70 BEGIN_NCBI_SCOPE
71
72 DEFINE_STATIC_FAST_MUTEX(s_ConfigMutex);
73 DEFINE_STATIC_FAST_MUTEX(s_NetworkDataCacheMutex);
74 DEFINE_STATIC_FAST_MUTEX(s_SeedLogConfigMutex);
75 DEFINE_STATIC_FAST_MUTEX(s_SingletonMutex);
76 DEFINE_STATIC_FAST_MUTEX(s_KnownConnMutex);
77 static CSocket* s_TimeoutingSock = NULL;
78 static CSocket* s_PeerSock = NULL;
79 const int kRandCount = 100;
80 /* Registry names */
81 const string kMonkeyMainSect = "CHAOS_MONKEY";
82 const string kEnablField = "enabled";
83 const string kSeedField = "seed";
84
85 #define PARAM_TWICE_EXCEPTION(param) \
86 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__), \
87 NULL, CMonkeyException::e_MonkeyInvalidArgs, \
88 string("Parameter \"" param "\" is set " \
89 "twice in [") + GetSection() + "]" + \
90 s_RuleTypeString(GetActionType()));
91
92
93 /*/////////////////////////////////////////////////////////////////////////////
94 // MOCK DEFINITIONS //
95 /////////////////////////////////////////////////////////////////////////////*/
96 # ifdef NCBI_MONKEY_TESTS
97 # undef DECLARE_MONKEY_MOCK
98 /* Declare a static variable and global getter&setter for it */
99 # define DECLARE_MONKEY_MOCK(ty,name,def_val) \
100 static ty s_Monkey_ ## name = def_val; \
101 ty g_MonkeyMock_Get ## name() { \
102 return s_Monkey_ ## name; \
103 } \
104 void g_MonkeyMock_Set ## name(const ty& val) { \
105 s_Monkey_ ## name = val; \
106 }
107
108 /* This macro contains the list of variables to be mocked.
109 * Needed mocks will be created with DECLARE_MONKEY_MOCK */
110 MONKEY_MOCK_MACRO()
111
112 string CMonkeyMocks::MainMonkeySection = "CHAOS_MONKEY";
113
114
Reset()115 void CMonkeyMocks::Reset()
116 {
117 MainMonkeySection = "CHAOS_MONKEY";
118 }
119
GetMainMonkeySection()120 string CMonkeyMocks::GetMainMonkeySection()
121 {
122 return MainMonkeySection;
123 }
SetMainMonkeySection(string section)124 void CMonkeyMocks::SetMainMonkeySection(string section)
125 {
126 MainMonkeySection = section;
127 }
128
129
130 /*/////////////////////////////////////////////////////////////////////////////
131 // CMonkeyException //
132 /////////////////////////////////////////////////////////////////////////////*/
what() const133 const char* CMonkeyException::what() const throw()
134 {
135
136 return CException::GetMsg().c_str();
137 }
138
139
140 # endif /* NCBI_MONKEY_TESTS */
141
142
143 /*/////////////////////////////////////////////////////////////////////////////
144 // STATIC CONVENIENCE FUNCTIONS //
145 /////////////////////////////////////////////////////////////////////////////*/
s_Monkey_Split(const string & s,char delim,vector<string> & elems)146 static vector<string>& s_Monkey_Split(const string &s,
147 char delim,
148 vector<string> &elems)
149 {
150 stringstream ss(s);
151 string item;
152 while (getline(ss, item, delim)) {
153 elems.push_back(item);
154 }
155 return elems;
156 }
157
158
159 /* Trash collector for Thread Local Storage */
160 template<class T>
s_TlsCleanup(T * p_value,void *)161 static void s_TlsCleanup(T* p_value, void* /* data */)
162 {
163 delete p_value;
164 }
165
166
s_TimeoutingSocketInit(void)167 static void s_TimeoutingSocketInit(void)
168 {
169 unsigned short i = 8080;
170 CListeningSocket* server_socket;
171
172 for (; i < 8100; i++) {
173 /* Initialize a timing out socket */
174 try {
175 server_socket = new CListeningSocket(i);
176 } catch (CException) {
177 continue;
178 }
179 STimeout accept_timeout = { 0, 20000 };
180 STimeout connect_timeout = { 1, 20000 };
181 if (server_socket->GetStatus() != eIO_Success) {
182 server_socket->Close();
183 delete server_socket;
184 continue;
185 }
186 try {
187 s_TimeoutingSock = new CSocket(CSocketAPI::gethostbyaddr(0), i,
188 &connect_timeout);
189 } catch (CException) {
190 continue;
191 }
192 s_PeerSock = new CSocket;
193 if (server_socket->Accept(*s_PeerSock, &accept_timeout) == eIO_Success) {
194 server_socket->Close();
195 delete server_socket;
196 char buf[1024];
197 size_t n_read;
198 s_TimeoutingSock->SetTimeout(eIO_Read, &accept_timeout);
199 s_TimeoutingSock->SetTimeout(eIO_Write, &accept_timeout);
200 s_TimeoutingSock->Read((void*)buf, 1024, &n_read);
201 return;
202 } else {
203 s_TimeoutingSock->Close();
204 delete s_TimeoutingSock;
205 s_PeerSock->Close();
206 delete s_PeerSock;
207 server_socket->Close();
208 delete server_socket;
209 continue;
210 }
211 /* If we got here, everything works fine */
212 }
213 throw CMonkeyException(CDiagCompileInfo(), NULL,
214 CMonkeyException::e_MonkeyUnknown,
215 "Could not create a peer socket for the "
216 "timeouting socket. Tried ports 8080-8100",
217 ncbi::EDiagSev::eDiagSevMin);
218 }
219
220
s_TimeoutingSocketDestroy(void)221 static void s_TimeoutingSocketDestroy(void)
222 {
223 if (s_TimeoutingSock != NULL) {
224 s_TimeoutingSock->Close();
225 delete s_TimeoutingSock;
226 s_TimeoutingSock = NULL;
227 }
228 if (s_PeerSock != NULL) {
229 s_PeerSock->Close();
230 delete s_PeerSock;
231 s_PeerSock = NULL;
232 }
233 }
234
235
s_GetMonkeySection()236 static string s_GetMonkeySection()
237 {
238 CNcbiRegistry& config = CNcbiApplication::Instance()->GetConfig();
239 return config.Get(CMonkeyMocks::GetMainMonkeySection(), "config");
240 }
241
242
s_Monkey_Split(const string & s,char delim)243 static vector<string> s_Monkey_Split(const string &s, char delim) {
244 vector<string> elems;
245 s_Monkey_Split(s, delim, elems);
246 return elems;
247 }
248
249
s_MONKEY_GenRandomString(char * s,const size_t len)250 static void s_MONKEY_GenRandomString(char *s, const size_t len) {
251 static const char alphabet[] =
252 "0123456789"
253 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
254 "abcdefghijklmnopqrstuvwxyz"
255 " !@#$%^&*()-=_+/,.{}[];':`~<>"
256 "\\\n\"";
257
258 for (size_t i = 0; i < len - 1; ++i) {
259 s[i] = alphabet[rand() % (sizeof(alphabet) - 1)];
260 }
261
262 s[len-1] = 0;
263 }
264
265
x_GetSocketDestinations(MONKEY_SOCKTYPE sock,string * fqdn,string * IP,unsigned short * my_port,unsigned short * peer_port)266 void CMonkey::x_GetSocketDestinations(MONKEY_SOCKTYPE sock,
267 string* fqdn,
268 string* IP,
269 unsigned short* my_port,
270 unsigned short* peer_port)
271 {
272 struct sockaddr_in sock_addr;
273 #ifdef NCBI_OS_MSWIN
274 int len_inet = sizeof(sock_addr);
275 #else
276 socklen_t len_inet = sizeof(sock_addr);
277 #endif
278 if (my_port != NULL) {
279 getsockname(sock, (struct sockaddr*)&sock_addr, &len_inet);
280 *my_port = ntohs(sock_addr.sin_port);
281 }
282 /* If we need peer information for at least one value */
283 if (peer_port || fqdn || IP)
284 getpeername(sock, (struct sockaddr*)&sock_addr, &len_inet);
285 if (peer_port != NULL)
286 *peer_port = ntohs(sock_addr.sin_port);
287 #ifndef NCBI_OS_MSWIN
288 uint32_t addr = sock_addr.sin_addr.s_addr;
289 #else
290 u_long addr = sock_addr.sin_addr.S_un.S_addr;
291 #endif
292 SFqdnIp&& net_data = x_GetFqdnIp(addr);
293 if (fqdn != NULL)
294 *fqdn = net_data.fqdn;
295 if (IP != NULL)
296 *IP = net_data.ip;
297 }
298
299
300 #if 0
301 # ifdef NCBI_OS_MSWIN
302 static
303 int s_MONKEY_GetTimeOfDay(struct timeval* tv)
304 {
305 FILETIME systime;
306 unsigned __int64 sysusec;
307
308 if (!tv)
309 return -1;
310
311 GetSystemTimeAsFileTime(&systime);
312
313 sysusec = systime.dwHighDateTime;
314 sysusec <<= 32;
315 sysusec |= systime.dwLowDateTime;
316 sysusec += 5;
317 sysusec /= 10;
318
319 tv->tv_usec = (long)(sysusec % 1000000);
320 tv->tv_sec = (long)(sysusec / 1000000 - 11644473600Ui64);
321
322 return 0;
323 }
324
325 # else
326
327 # define s_MONKEY_GetTimeOfDay(tv) gettimeofday(tv, 0)
328
329 # endif
330
331 static
332 double s_MONKEY_TimeDiff(const struct timeval* end,
333 const struct timeval* beg)
334 {
335 if (end->tv_sec < beg->tv_sec)
336 return 0.0;
337 if (end->tv_usec < beg->tv_usec) {
338 if (end->tv_sec == beg->tv_sec)
339 return 0.0;
340 return (end->tv_sec - beg->tv_sec - 1)
341 + (end->tv_usec - beg->tv_usec + 1000000) / 1000000.0;
342 }
343 return (end->tv_sec - beg->tv_sec)
344 + (end->tv_usec - beg->tv_usec) / 1000000.0;
345 }
346 #endif
347
348
349 /*//////////////////////////////////////////////////////////////////////////////
350 // CMonkeySeedKey //
351 //////////////////////////////////////////////////////////////////////////////*/
Key()352 const CMonkeySeedKey& CMonkeySeedAccessor::Key()
353 {
354 static CMonkeySeedKey key;
355 return key;
356 }
357
358
359 /*//////////////////////////////////////////////////////////////////////////////
360 // CMonkeyRuleBase //
361 //////////////////////////////////////////////////////////////////////////////*/
s_RuleTypeString(EMonkeyActionType type)362 static string s_RuleTypeString(EMonkeyActionType type)
363 {
364 switch (type) {
365 case eMonkey_Connect:
366 return "connect";
367 case eMonkey_Poll:
368 return "poll";
369 case eMonkey_Recv:
370 return "read";
371 case eMonkey_Send:
372 return "write";
373 default:
374 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
375 NULL, CMonkeyException::e_MonkeyInvalidArgs,
376 string("Unknown EMonkeyActionType value"));
377 }
378 }
379
380
s_SocketCallString(EMonkeyActionType action)381 static string s_SocketCallString(EMonkeyActionType action)
382 {
383 switch (action) {
384 case eMonkey_Recv:
385 return "recv()";
386 case eMonkey_Send:
387 return "send()";
388 case eMonkey_Poll:
389 return "poll()";
390 case eMonkey_Connect:
391 return "connect()";
392 default:
393 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
394 NULL, CMonkeyException::e_MonkeyInvalidArgs,
395 string("Unknown EMonkeyActionType value"));
396 }
397 }
398
399
s_EIOStatusString(EIO_Status status)400 static string s_EIOStatusString(EIO_Status status)
401 {
402 switch (status) {
403 case eIO_Timeout:
404 return "eIO_Timeout";
405 case eIO_Closed:
406 return "eIO_Closed";
407 case eIO_Unknown:
408 return "eIO_Unknown";
409 case eIO_Interrupt:
410 return "eIO_Interrupt";
411 default:
412 break;
413 }
414 }
415
416
CMonkeyRuleBase(EMonkeyActionType action_type,string section,const vector<string> & params)417 CMonkeyRuleBase::CMonkeyRuleBase(EMonkeyActionType action_type,
418 string section,
419 const vector<string>& params)
420 : m_ReturnStatus(-1), m_RepeatType(eMonkey_RepeatNone), m_Delay (0),
421 m_RunsSize(0), m_ActionType(action_type), m_Section(section)
422 {
423 /** If there are no-interception runs before repeating the cycle,
424 * we know that from m_RunsSize */
425 for (unsigned int i = 0; i < params.size(); i++) {
426 vector<string> name_value = s_Monkey_Split(params[i], '=');
427 string name = name_value[0];
428 string value = name_value[1];
429 if (name == "return_status") {
430 x_ReadEIOStatus(value);
431 } else if (name == "runs") {
432 x_ReadRuns(value);
433 } else if (name == "delay") {
434 m_Delay = NStr::StringToULong(value);
435 }
436 }
437 }
438
439
AddSocket(MONKEY_SOCKTYPE sock)440 void CMonkeyRuleBase::AddSocket(MONKEY_SOCKTYPE sock)
441 {
442 /* Element has to exist */
443 m_RunPos[sock];
444 }
445
446 /** Check that the rule should trigger on this run */
CheckRun(MONKEY_SOCKTYPE sock,unsigned short rule_probability,unsigned short probability_left) const447 bool CMonkeyRuleBase::CheckRun(MONKEY_SOCKTYPE sock,
448 unsigned short rule_probability,
449 unsigned short probability_left) const
450 {
451 bool isRun = false;
452
453 int rand_val = CMonkey::Instance()->GetRand(Key()) % 100;
454 isRun = (rand_val) < rule_probability * 100 / probability_left;
455 LOG_POST(Note << "[CMonkeyRuleBase::CheckRun] Checking if the rule "
456 << "for " << s_SocketCallString(m_ActionType)
457 << " will be run this time. Random value is "
458 << rand_val << ", probability threshold is "
459 << rule_probability * 100 / probability_left);
460 LOG_POST(Note << "[CMonkeyRuleBase::CheckRun] The rule will be "
461 << (isRun ? "" : "NOT ") << "run");
462 return isRun;
463 }
464
465
GetProbability(MONKEY_SOCKTYPE sock) const466 unsigned short CMonkeyRuleBase::GetProbability(MONKEY_SOCKTYPE sock) const
467 {
468 if (m_RunPos.find(sock) == m_RunPos.end()) {
469 throw CMonkeyException(
470 CDiagCompileInfo(__FILE__, __LINE__),
471 NULL, CMonkeyException::e_MonkeyInvalidArgs,
472 "The socket provided has not been registered with current rule");
473 }
474 /* If 'runs' not set, the rule always engages */
475 if (m_RunsSize == 0) {
476 return 100;
477 }
478 if (m_RunMode == eMonkey_RunProbability) {
479 return static_cast<unsigned short>(m_Runs.at(m_RunPos.at(sock)) * 100);
480 } else {
481 /* If current run is more than the maximum run in the rule, we have to
482 * start over, or stop*/
483 if ((double)(m_RunPos.at(sock) + 1) > m_Runs.back()) {
484 switch (m_RepeatType) {
485 case eMonkey_RepeatNone:
486 return 0;
487 case eMonkey_RepeatLast:
488 return 100;
489 }
490 }
491 /* If m_Runs has the exact number of current run, the rule triggers */
492 return std::binary_search(m_Runs.begin(),
493 m_Runs.end(),
494 m_RunPos.at(sock) + 1) ? 100 : 0;
495 }
496 /* If "runs" is not set, rule always triggers */
497 if (m_Runs.empty()) return 100;
498
499 }
500
501
x_ReadEIOStatus(const string & eIOStatus_in)502 void CMonkeyRuleBase::x_ReadEIOStatus(const string& eIOStatus_in)
503 {
504 /* Check that 'runs' has not been set before */
505 if (m_ReturnStatus != -1) {
506 PARAM_TWICE_EXCEPTION("return_status");
507 }
508 string eIOStatus = eIOStatus_in;
509 NStr::ToLower(eIOStatus);
510 if (eIOStatus == "eio_closed") {
511 m_ReturnStatus = eIO_Closed;
512 } else if (eIOStatus == "eio_invalidarg") {
513 throw CMonkeyException(
514 CDiagCompileInfo(__FILE__, __LINE__),
515 NULL, CMonkeyException::e_MonkeyInvalidArgs,
516 string("Unsupported 'return_status': ") + eIOStatus_in);
517 } else if (eIOStatus == "eio_interrupt") {
518 m_ReturnStatus = eIO_Interrupt;
519 } else if (eIOStatus == "eio_success") {
520 m_ReturnStatus = eIO_Success;
521 } else if (eIOStatus == "eio_timeout") {
522 m_ReturnStatus = eIO_Timeout;
523 } else if (eIOStatus == "eio_unknown") {
524 m_ReturnStatus = eIO_Unknown;
525 } else if (eIOStatus == "eio_notsupported") {
526 m_ReturnStatus = eIO_NotSupported;
527 } else {
528 throw CMonkeyException(
529 CDiagCompileInfo(__FILE__, __LINE__),
530 NULL, CMonkeyException::e_MonkeyInvalidArgs,
531 string("Could not parse 'return_status': ") + eIOStatus_in);
532 }
533 }
534
535
x_ReadRuns(const string & runs)536 void CMonkeyRuleBase::x_ReadRuns(const string& runs)
537 {
538 /* Check that 'runs' has not been set before */
539 if (m_RunsSize != 0) {
540 PARAM_TWICE_EXCEPTION("runs");
541 }
542 /* We get the string already without whitespaces and only have to
543 split it on commas*/
544 vector<string> runs_list = s_Monkey_Split(runs, ',');
545 if (runs_list.size() == 0)
546 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
547 NULL, CMonkeyException::e_MonkeyInvalidArgs,
548 string("Parameter \"runs\" is empty in [")
549 + m_Section + "]" +
550 s_RuleTypeString(m_ActionType));
551 m_RunMode = runs_list[0][runs_list[0].length() - 1] == '%'
552 ? eMonkey_RunProbability : eMonkey_RunNumber;
553
554 if (runs_list.size() == 1 && m_RunMode == eMonkey_RunProbability)
555 runs_list.push_back("...");
556 m_RepeatType = eMonkey_RepeatNone;
557 if ( *runs_list.rbegin() == "repeat" ) {
558 m_RepeatType = eMonkey_RepeatAgain;
559 } else if ( *runs_list.rbegin() == "..." ) {
560 m_RepeatType = eMonkey_RepeatLast;
561 }
562
563 ERunFormat run_format = runs_list[0].find_first_of(':') != string::npos ?
564 eMonkey_RunRanges :
565 eMonkey_RunSequence;
566
567 unsigned int end_pos = (m_RepeatType == eMonkey_RepeatNone)
568 ? runs_list.size() : runs_list.size() - 1;
569
570 for ( unsigned int i = 0; i < end_pos; i++ ) {
571 string& run = runs_list[i];
572 double prob;
573 if (m_RunMode == eMonkey_RunProbability) {
574 if (*run.rbegin() != '%') {
575 throw CMonkeyException(
576 CDiagCompileInfo(__FILE__, __LINE__),
577 NULL, CMonkeyException::e_MonkeyInvalidArgs,
578 "Value is not percentage: " + run +
579 ", values have to be either only percentages or "
580 "only numbers of tries, not mixed");
581 }
582 switch (run_format) {
583 default:
584 assert(0);
585 /* In release build - fall through to eMonkey_RunSequence */
586 case eMonkey_RunSequence:
587 prob = NStr::StringToDouble(run.substr(0, run.length() - 1));
588 m_Runs.push_back(prob / 100);
589 break;
590 case eMonkey_RunRanges:
591 size_t prob_start = run.find_first_of(':');
592 size_t step = NStr::StringToSizet(run.substr(0, prob_start));
593 size_t last_step = m_Runs.size();
594 if (last_step == 0 && step != 1) {
595 throw CMonkeyException(
596 CDiagCompileInfo(__FILE__, __LINE__),
597 NULL, CMonkeyException::e_MonkeyInvalidArgs,
598 "In the string of runs: " + runs + " the first "
599 "element MUST set value for the first run");
600 }
601 prob = NStr::StringToDouble(run.substr(prob_start + 1,
602 run.length() - prob_start - 2));
603 for (size_t j = last_step+1; j > 0 && j < step; j++) {
604 m_Runs.push_back(*m_Runs.rbegin());
605 }
606 m_Runs.push_back(prob / 100);
607 break;
608 }
609 } else {
610 assert(run_format == eMonkey_RunSequence);
611 if (*run.rbegin() == '%') {
612 throw CMonkeyException(
613 CDiagCompileInfo(__FILE__, __LINE__),
614 NULL, CMonkeyException::e_MonkeyInvalidArgs,
615 string("Value is percentage: ") + run +
616 string(", values have to be either only percentages or "
617 "only numbers of tries, not mixed"));
618 }
619 int val = NStr::StringToInt(run);
620 if (m_Runs.size() > 0 && val <= *m_Runs.rbegin()) {
621 throw CMonkeyException(
622 CDiagCompileInfo(__FILE__, __LINE__),
623 NULL, CMonkeyException::e_MonkeyInvalidArgs,
624 string("\"runs\" should contain values in "
625 "increasing order"));
626 }
627 m_Runs.push_back(val);
628 }
629 }
630 m_RunsSize = m_Runs.size();
631 }
632
633
GetReturnStatus() const634 int /* EIO_Status or -1 */ CMonkeyRuleBase::GetReturnStatus() const
635 {
636 return m_ReturnStatus;
637 }
638
639
GetDelay() const640 unsigned long CMonkeyRuleBase::GetDelay() const
641 {
642 return m_Delay;
643 }
644
645
GetSection(void) const646 std::string CMonkeyRuleBase::GetSection(void) const
647 {
648 return m_Section;
649 }
650
651
GetActionType(void) const652 EMonkeyActionType CMonkeyRuleBase::GetActionType(void) const
653 {
654 return m_ActionType;
655 }
656
657
IterateRun(MONKEY_SOCKTYPE sock)658 void CMonkeyRuleBase::IterateRun(MONKEY_SOCKTYPE sock)
659 {
660 if (m_Runs.empty())
661 return;
662 m_RunPos[sock]++;
663 /* In probability mode each item of m_Runs is a probability of each run */
664 if (m_RunMode == eMonkey_RunProbability) {
665 assert(m_RunPos[sock] <= m_Runs.size());
666 if (m_RunPos[sock] == m_Runs.size()) {
667 switch (m_RepeatType) {
668 case eMonkey_RepeatNone: case eMonkey_RepeatLast:
669 m_RunPos[sock]--;
670 case eMonkey_RepeatAgain:
671 m_RunPos[sock] = 0;
672 break;
673 }
674 }
675 return;
676 }
677 /* In "number of the run" mode each item in m_Runs is a specific number of
678 run when the rule should trigger */
679 else if ((m_RunPos.at(sock) + 1) > *m_Runs.rbegin()) {
680 switch (m_RepeatType) {
681 case eMonkey_RepeatAgain:
682 m_RunPos[sock] = 0;
683 break;
684 }
685 }
686 return;
687 }
688
689
690 /*//////////////////////////////////////////////////////////////////////////////
691 // CMonkeyRWRuleBase //
692 //////////////////////////////////////////////////////////////////////////////*/
CMonkeyRWRuleBase(EMonkeyActionType action_type,string section,const vector<string> & params)693 CMonkeyRWRuleBase::CMonkeyRWRuleBase(EMonkeyActionType action_type,
694 string section,
695 const vector<string>& params)
696 : CMonkeyRuleBase(action_type, section, params)
697 {
698 }
699
700
701 //////////////////////////////////////////////////////////////////////////
702 // CMonkeyWriteRule
703 //////////////////////////////////////////////////////////////////////////
CMonkeyWriteRule(string section,const vector<string> & params)704 CMonkeyWriteRule::CMonkeyWriteRule(string section, const vector<string>& params)
705 : CMonkeyRWRuleBase(eMonkey_Send, section, params)
706 {
707 }
708
709
Run(MONKEY_SOCKTYPE sock,const MONKEY_DATATYPE data,MONKEY_LENTYPE size,int flags,SOCK * sock_ptr)710 MONKEY_RETTYPE CMonkeyWriteRule::Run(MONKEY_SOCKTYPE sock,
711 const MONKEY_DATATYPE data,
712 MONKEY_LENTYPE size,
713 int flags,
714 SOCK* sock_ptr)
715 {
716 #ifdef NCBI_MONKEY_TESTS
717 g_MonkeyMock_SetInterceptedSend(true);
718 #endif /* NCBI_MONKEY_TESTS */
719 int return_status = GetReturnStatus();
720 LOG_POST(Error << "[CMonkeyReadRule::Run] CHAOS MONKEY ENGAGE!!! "
721 "INTERCEPTED send(). " <<
722 (return_status != -1
723 ? s_EIOStatusString((EIO_Status)return_status)
724 : string()));
725 if (return_status != eIO_Success && return_status != -1) {
726 switch (return_status) {
727 case eIO_Timeout:
728 *sock_ptr = s_TimeoutingSock->GetSOCK();
729 MONKEY_SET_SOCKET_ERROR(SOCK_EWOULDBLOCK);
730 return -1;
731 case eIO_Closed:
732 MONKEY_SET_SOCKET_ERROR(SOCK_ENOTCONN);
733 return -1;
734 case eIO_Unknown:
735 MONKEY_SET_SOCKET_ERROR(MONKEY_ENOPROTOOPT);
736 return -1;
737 case eIO_Interrupt:
738 MONKEY_SET_SOCKET_ERROR(SOCK_EINTR);
739 return -1;
740 default:
741 break;
742 }
743 }
744
745 return send(sock, (const char*)data, size, flags);
746 }
747
748
749 //////////////////////////////////////////////////////////////////////////
750 // CMonkeyReadRule
751 //////////////////////////////////////////////////////////////////////////
CMonkeyReadRule(string section,const vector<string> & params)752 CMonkeyReadRule::CMonkeyReadRule(string section, const vector<string>& params)
753 : CMonkeyRWRuleBase(eMonkey_Recv, section, params)
754 {
755 STimeout r_timeout = { 1, 0 };
756 }
757
758
Run(MONKEY_SOCKTYPE sock,MONKEY_DATATYPE buf,MONKEY_LENTYPE size,int flags,SOCK * sock_ptr)759 MONKEY_RETTYPE CMonkeyReadRule::Run(MONKEY_SOCKTYPE sock,
760 MONKEY_DATATYPE buf,
761 MONKEY_LENTYPE size,
762 int flags,
763 SOCK* sock_ptr)
764 {
765 #ifdef NCBI_MONKEY_TESTS
766 g_MonkeyMock_SetInterceptedRecv(true);
767 #endif /* NCBI_MONKEY_TESTS */
768 int return_status = GetReturnStatus();
769 LOG_POST(Error << "[CMonkeyReadRule::Run] CHAOS MONKEY ENGAGE!!! "
770 "INTERCEPTED recv(). " <<
771 (return_status != -1
772 ? s_EIOStatusString((EIO_Status)return_status)
773 : string()));
774 if (return_status != eIO_Success && return_status != -1) {
775 switch (return_status) {
776 case eIO_Timeout:
777 *sock_ptr = s_TimeoutingSock->GetSOCK();
778 MONKEY_SET_SOCKET_ERROR(SOCK_EWOULDBLOCK);
779 return -1;
780 case eIO_Closed:
781 MONKEY_SET_SOCKET_ERROR(SOCK_ENOTCONN);
782 return -1;
783 case eIO_Unknown:
784 MONKEY_SET_SOCKET_ERROR(MONKEY_ENOPROTOOPT);
785 return -1;
786 case eIO_Interrupt:
787 MONKEY_SET_SOCKET_ERROR(SOCK_EINTR);
788 default:
789 break;
790 }
791 }
792
793 if (size == 0)
794 return 0;
795
796 /* So we decided to override */
797 MONKEY_RETTYPE bytes_read = recv(sock, buf, size, flags);
798
799 return bytes_read;
800 }
801
802
803 //////////////////////////////////////////////////////////////////////////
804 // CMonkeyConnectRule
805 //////////////////////////////////////////////////////////////////////////
806
CMonkeyConnectRule(string section,const vector<string> & params)807 CMonkeyConnectRule::CMonkeyConnectRule(string section,
808 const vector<string>& params)
809 : CMonkeyRuleBase(eMonkey_Connect, section, params)
810 {
811 bool allow_set = false;
812 for (unsigned int i = 0; i < params.size(); i++) {
813 vector<string> name_value = s_Monkey_Split(params[i], '=');
814 string name = name_value[0];
815 string value = name_value[1];
816 if (name == "allow") {
817 if (allow_set) {
818 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
819 NULL,
820 CMonkeyException::e_MonkeyInvalidArgs,
821 string("Parameter \"allow\" is set "
822 "twice in [") + GetSection() + "]" +
823 s_RuleTypeString(GetActionType()));
824 }
825 m_Allow = ConnNetInfo_Boolean(value.c_str()) == 1;
826 allow_set = true;
827 }
828 }
829 if (!allow_set)
830 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
831 NULL, CMonkeyException::e_MonkeyInvalidArgs,
832 string("Parameter \"allow\" not set in [")
833 + GetSection() + "]" +
834 s_RuleTypeString(GetActionType()));
835 }
836
837
Run(MONKEY_SOCKTYPE sock,const struct sockaddr * name,MONKEY_SOCKLENTYPE namelen)838 int CMonkeyConnectRule::Run(MONKEY_SOCKTYPE sock,
839 const struct sockaddr* name,
840 MONKEY_SOCKLENTYPE namelen)
841 {
842 #ifdef NCBI_MONKEY_TESTS
843 g_MonkeyMock_SetInterceptedConnect(true);
844 #endif /* NCBI_MONKEY_TESTS */
845 int return_status = GetReturnStatus();
846 LOG_POST(Error << "[CMonkeyReadRule::Run] CHAOS MONKEY ENGAGE!!! "
847 "INTERCEPTED connect(). " <<
848 (return_status != -1
849 ? s_EIOStatusString((EIO_Status)return_status)
850 : string()));
851 if (return_status != eIO_Success && return_status != -1) {
852 switch (return_status) {
853 case eIO_Timeout:
854 MONKEY_SET_SOCKET_ERROR(SOCK_EWOULDBLOCK);
855 return -1;
856 case eIO_Closed:
857 MONKEY_SET_SOCKET_ERROR(SOCK_ECONNREFUSED);
858 return -1;
859 case eIO_Unknown:
860 MONKEY_SET_SOCKET_ERROR(MONKEY_ENOPROTOOPT);
861 return -1;
862 case eIO_Interrupt:
863 MONKEY_SET_SOCKET_ERROR(SOCK_EINTR);
864 return -1;
865 default:
866 break;
867 }
868 }
869 if (GetDelay() > 0) {
870 SleepMilliSec(GetDelay(), EInterruptOnSignal::eInterruptOnSignal);
871 }
872 if (m_Allow) {
873 return connect(sock, name, namelen);
874 }
875 else {
876 struct sockaddr_in addr;
877 /* Connect to a non-existing host */
878 addr.sin_family = AF_INET;
879 addr.sin_port = 65511;
880 /* 3232235631 is 192.168.0.111 - does not exist at NCBI, which is
881 enough for the goal of test */
882 #ifdef NCBI_OS_MSWIN
883 addr.sin_addr.S_un.S_addr = htonl(3232235631);
884 #else
885 addr.sin_addr.s_addr = htonl(3232235631);
886 #endif /* NCBI_OS_MSWIN */
887 return connect(sock, (struct sockaddr*)&addr, namelen);
888 }
889 }
890
891
892 //////////////////////////////////////////////////////////////////////////
893 // CMonkeyPollRule
894 //////////////////////////////////////////////////////////////////////////
CMonkeyPollRule(string section,const vector<string> & params)895 CMonkeyPollRule::CMonkeyPollRule(string section, const vector<string>& params)
896 : CMonkeyRuleBase(eMonkey_Poll, section, params)
897 {
898 bool ignore_set = false;
899 for ( unsigned int i = 0; i < params.size(); i++ ) {
900 vector<string> name_value = s_Monkey_Split(params[i], '=');
901 string name = name_value[0];
902 string value = name_value[1];
903 if (name == "ignore") {
904 if (ignore_set) {
905 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
906 NULL,
907 CMonkeyException::e_MonkeyInvalidArgs,
908 string("Parameter \"ignore\" is set "
909 "twice in [") + GetSection() + "]" +
910 s_RuleTypeString(GetActionType()));
911 }
912 m_Ignore = ConnNetInfo_Boolean(value.c_str()) == 1;
913 ignore_set = true;
914 }
915 }
916 if (!ignore_set) {
917 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
918 NULL, CMonkeyException::e_MonkeyInvalidArgs,
919 string("Parameter \"ignore\" not set in [")
920 + GetSection() + "]" +
921 s_RuleTypeString(GetActionType()));
922 }
923 }
924
925
Run(size_t * n,SOCK * sock,EIO_Status * return_status)926 bool CMonkeyPollRule::Run(size_t* n,
927 SOCK* sock,
928 EIO_Status* return_status)
929 {
930 #ifdef NCBI_MONKEY_TESTS
931 g_MonkeyMock_SetInterceptedPoll(true);
932 #endif /* NCBI_MONKEY_TESTS */
933 LOG_POST(Error << "[CMonkeyReadRule::Run] CHAOS MONKEY ENGAGE!!! "
934 "INTERCEPTED poll(). " <<
935 (m_Ignore ? "Ignoring poll" : "NOT ignoring poll"));
936 if (m_Ignore) {
937 return true;
938 }
939 return false;
940 }
941
942
943 //////////////////////////////////////////////////////////////////////////
944 // CMonkeyPlan
945 //////////////////////////////////////////////////////////////////////////
CMonkeyPlan(const string & section)946 CMonkeyPlan::CMonkeyPlan(const string& section)
947 : m_Probability(100), m_HostRegex(".*"), m_Name(section)
948 {
949 /* Plan settings */
950 CNcbiRegistry& config = CNcbiApplication::Instance()->GetConfig();
951 string probability = config.Get(section, "probability");
952 if (probability != "") {
953 if (probability.find('%') != string::npos) {
954 probability = probability.substr(0, probability.length() - 1);
955 m_Probability = static_cast<unsigned short>(
956 NStr::StringToUInt(probability));
957 } else {
958 m_Probability = static_cast<unsigned short>(
959 NStr::StringToDouble(probability) * 100);
960 }
961 }
962 m_HostRegex = NStr::Replace(NStr::Replace(
963 config.Get(section, "host_match"), " ", ""),
964 "\t", "");
965
966 x_ReadPortRange(config.Get(section, "port_match"));
967
968 /* Write rules */
969 x_LoadRules(section, "write", m_WriteRules);
970 /* Read rules */
971 x_LoadRules(section, "read", m_ReadRules);
972 /* Connect rules */
973 x_LoadRules(section, "connect", m_ConnectRules);
974 /* Poll rules */
975 x_LoadRules(section, "poll", m_PollRules);
976 }
977
978
979 template<class Rule_Ty>
x_LoadRules(const string & section,const string & rule_type_str,vector<Rule_Ty> & container)980 void CMonkeyPlan::x_LoadRules(const string& section,
981 const string& rule_type_str,
982 vector<Rule_Ty>& container)
983 {
984 CNcbiRegistry& config = CNcbiApplication::Instance()->GetConfig();
985 bool multi_rule = false;
986 string rule_str = config.Get(section, rule_type_str);
987 if (rule_str == "") {
988 /* Try check if there are multiple rules defined */
989 if ((rule_str = config.Get(section, rule_type_str + "1")) != "")
990 multi_rule = true;
991 }
992 if ( rule_str == "" )
993 return;
994 /* If a rule was read - parse it */
995 string temp_conf = NStr::Replace(rule_str, string(" "), string(""));
996 vector<string> params = s_Monkey_Split(temp_conf, ';');
997 container.push_back(Rule_Ty(section, params));
998 if ( multi_rule ) {
999 int rule_num = 2;
1000 string val_name = rule_type_str + NStr::IntToString(rule_num++);
1001 while ( (rule_str = config.Get(section, val_name)) != "" ) {
1002 temp_conf = NStr::Replace(rule_str, string(" "), string(""));
1003 params = s_Monkey_Split(temp_conf, ';');
1004 container.push_back(Rule_Ty(section, params));
1005 val_name = rule_type_str+ NStr::IntToString(rule_num++);
1006 }
1007 }
1008 }
1009
1010
1011 #ifndef NCBI_OS_MSWIN
1012 /** Regex-like check (might use some refactoring) */
s_MatchRegex(const string & to_match,const string & regex)1013 static bool s_MatchRegex(const string& to_match, const string& regex)
1014 {
1015 /* Convert "match regex" task to "haystack and needle" task. We check
1016 * whether there is a wildcard in the beginning and/or in the end of
1017 * regex (other cases are not supported). And then check if to_match
1018 * starts with, end with or contains the needle */
1019
1020 /* First - compatibility check. Functionality is limited. */
1021 /* Check for special characters and combinations that cannot be processed.
1022 * Method - remove combinations of characters that are supported and check
1023 * the rest */
1024 string exception_message = string("Pattern") + regex + " "
1025 "cannot be processed because regular expressions "
1026 "are not fully supported. You can use .* in the "
1027 "beginning and in the end of a pattern and | to separate "
1028 "patterns. Stop mark is set with \\\\.\n"
1029 "Example:\n"
1030 "host_match = .*nlm\\\\.nih.*|.*gov|10\\\\.55.*\n";
1031 string filtered = NStr::Replace(NStr::Replace(regex, "\\.", ""),
1032 ".*" , "");
1033 if (filtered.find_first_of("[]()+^?{}$.*\\") != string::npos) {
1034 throw CMonkeyException(
1035 CDiagCompileInfo(__FILE__, __LINE__),
1036 NULL,
1037 CMonkeyException::e_MonkeyInvalidArgs,
1038 exception_message);
1039 }
1040 size_t pos = 0;
1041 size_t last_find = 0;
1042 bool start_wildcard = false, end_wildcard = false;
1043 while ((last_find = NStr::Find(regex, ".*", pos)) != NPOS &&
1044 pos < regex.length()) {
1045 pos = last_find + 1;
1046 if (last_find == 0) {
1047 start_wildcard = true;
1048 }
1049 else if (last_find == regex.length() - strlen(".*")) {
1050 end_wildcard = true;
1051 }
1052 else {
1053 throw CMonkeyException(
1054 CDiagCompileInfo(__FILE__, __LINE__),
1055 NULL,
1056 CMonkeyException::e_MonkeyInvalidArgs,
1057 exception_message);
1058 }
1059 }
1060 /*
1061 * Run the "Needle in a haystack" task
1062 */
1063 string needle = NStr::Replace(NStr::Replace(regex, ".*", ""), "\\.", ".");
1064 if (start_wildcard && !end_wildcard) {
1065 if (!NStr::StartsWith(to_match, needle)) {
1066 return false;
1067 }
1068 }
1069 else if (!start_wildcard && end_wildcard) {
1070 if (!NStr::EndsWith(to_match, needle)) {
1071 return false;
1072 }
1073 }
1074 else if (start_wildcard && end_wildcard) {
1075 if (NStr::Find(to_match, needle) == NPOS) {
1076 return false;
1077 }
1078 }
1079 return true;
1080 }
1081 #endif /* NCBI_OS_MSWIN */
1082
1083
1084 /* Compares supplied parameters to */
Match(const string & sock_host,unsigned short sock_port,const string & sock_url,unsigned short probability_left)1085 bool CMonkeyPlan::Match(const string& sock_host,
1086 unsigned short sock_port,
1087 const string& sock_url,
1088 unsigned short probability_left)
1089 {
1090 /* Check that regex works and that at least one of hostname and IP
1091 * is not empty*/
1092 if ( !m_HostRegex.empty() &&
1093 (sock_host.length() + sock_url.length() > 0) ) {
1094 #ifdef NCBI_OS_MSWIN
1095 regex reg(m_HostRegex);
1096 smatch find;
1097 if (!regex_match(sock_host, find, reg) &&
1098 !regex_match(sock_url, find, reg)) {
1099 LOG_POST(Note << "[CMonkeyPlan::Match] Plan " << m_Name << " was "
1100 << "NOT matched. Reason: "
1101 << "[host regex mismatch, host=" << sock_host
1102 << ", regex = \"" << m_HostRegex << "\"]");
1103 return false;
1104 }
1105 #else
1106 /*
1107 * Regex is not supported - work with simple simulation - only .* in
1108 * beginning and end of pattern and | to separate patterns
1109 */
1110 vector<string> patterns = s_Monkey_Split(m_HostRegex, '|');
1111 auto it = patterns.begin();
1112 bool match_found = false;
1113 for ( ; it != patterns.end(); it++) {
1114 if (s_MatchRegex(sock_url, *it) || s_MatchRegex(sock_host, *it)) {
1115 match_found = true;
1116 break;
1117 }
1118 }
1119 if (!match_found) {
1120 LOG_POST(Note << "[CMonkeyPlan::Match] Plan " << m_Name << " was "
1121 << "NOT matched. Reason: "
1122 << "[host regex mismatch, host=" << sock_host
1123 << ", regex = \"" << m_HostRegex << "\"]");
1124 return false;
1125 }
1126 #endif /* NCBI_OS_MSWIN */
1127 }
1128 /* If port match pattern is not set, any port is good */
1129 bool port_match = m_PortRanges.empty();
1130 auto port_iter = m_PortRanges.begin();
1131 for (; port_iter != m_PortRanges.end(); port_iter++) {
1132 if (sock_port >= *port_iter && sock_port <= *++port_iter) {
1133 port_match = true;
1134 break;
1135 }
1136 }
1137 int rand_val = CMonkey::Instance()->GetRand(Key()) % 100;
1138 bool prob_match = rand_val < m_Probability * 100 / probability_left;
1139 LOG_POST(Note << "[CMonkeyPlan::Match] Checking if plan " << m_Name
1140 << " will be matched. Random value is "
1141 << rand_val << ", plan probability is "
1142 << m_Probability << "%, probability left is "
1143 << probability_left << "%, so probability threshold is "
1144 << m_Probability * 100 / probability_left << "%");
1145 bool match = port_match && prob_match;
1146 LOG_POST(Note << "[CMonkeyPlan::Match] Plan " << m_Name << " was "
1147 << (match ? "" : "NOT ") << "matched"
1148 << (!match ? ". Reason: " : "")
1149 << (port_match ? "" : "[invalid port range] ")
1150 << (prob_match ? "" : "[probability threshold]"));
1151 return port_match ? (rand_val < m_Probability * 100 / probability_left)
1152 : false;
1153 }
1154
1155
WriteRule(MONKEY_SOCKTYPE sock,const MONKEY_DATATYPE data,MONKEY_LENTYPE size,int flags,MONKEY_RETTYPE * bytes_written,SOCK * sock_ptr)1156 bool CMonkeyPlan::WriteRule(MONKEY_SOCKTYPE sock,
1157 const MONKEY_DATATYPE data,
1158 MONKEY_LENTYPE size,
1159 int flags,
1160 MONKEY_RETTYPE* bytes_written,
1161 SOCK* sock_ptr)
1162 {
1163 short probability_left = 100;
1164 int res = -1;
1165 for (unsigned int i = 0; i < m_WriteRules.size(); i++) {
1166 m_WriteRules[i].AddSocket(sock);
1167 unsigned short rule_prob = m_WriteRules[i].GetProbability(sock);
1168 if (m_WriteRules[i].CheckRun(sock, rule_prob, probability_left)) {
1169 *bytes_written = m_WriteRules[i].Run(sock, data, size, flags,
1170 sock_ptr);
1171 res = 1;
1172 break;
1173 }
1174 /* If this rule did not engage, we go to the next rule, and
1175 remember to normalize probability of next rule */
1176 probability_left -= rule_prob;
1177 if (probability_left <= 0) {
1178 stringstream ss;
1179 ss << "Probability below zero for write rule in plan " << m_Name
1180 << ". Check config!";
1181 throw CMonkeyException(
1182 CDiagCompileInfo(__FILE__, __LINE__),
1183 NULL, CMonkeyException::e_MonkeyInvalidArgs,
1184 ss.str());
1185 }
1186 }
1187 // If no rules engaged, return 0
1188 if (res == -1)
1189 res = 0;
1190
1191 // Iterate run in all rules
1192 for (unsigned int i = 0; i < m_WriteRules.size(); i++) {
1193 m_WriteRules[i].IterateRun(sock);
1194 }
1195
1196 return res == 0 ? false : true;
1197 }
1198
1199
ReadRule(MONKEY_SOCKTYPE sock,MONKEY_DATATYPE buf,MONKEY_LENTYPE size,int flags,MONKEY_RETTYPE * bytes_read,SOCK * sock_ptr)1200 bool CMonkeyPlan::ReadRule(MONKEY_SOCKTYPE sock,
1201 MONKEY_DATATYPE buf,
1202 MONKEY_LENTYPE size,
1203 int flags,
1204 MONKEY_RETTYPE* bytes_read,
1205 SOCK* sock_ptr)
1206 {
1207 short probability_left = 100;
1208 int res = -1;
1209 for (unsigned int i = 0; i < m_ReadRules.size(); i++) {
1210 m_ReadRules[i].AddSocket(sock);
1211 unsigned short rule_prob = m_ReadRules[i].GetProbability(sock);
1212 if (m_ReadRules[i].CheckRun(sock, rule_prob, probability_left)) {
1213 *bytes_read = m_ReadRules[i].Run(sock, buf, size, flags, sock_ptr);
1214 res = 1;
1215 break;
1216 }
1217 /* If this rule did not engage, we go to the next rule, and
1218 and remember to normalize probability of next rule */
1219 probability_left -= rule_prob;
1220 if (probability_left <= 0) {
1221 stringstream ss;
1222 ss << "Probability below zero for write rule in plan " << m_Name
1223 << ". Check config!";
1224 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
1225 NULL, CMonkeyException::e_MonkeyInvalidArgs,
1226 ss.str());
1227 }
1228 }
1229
1230 // If no rules engaged, return 0
1231 if (res == -1)
1232 res = 0;
1233
1234 // Iterate run in all rules
1235 for (unsigned int i = 0; i < m_ReadRules.size(); i++) {
1236 m_ReadRules[i].IterateRun(sock);
1237 }
1238
1239 return res == 0 ? false : true;
1240 }
1241
1242
ConnectRule(MONKEY_SOCKTYPE sock,const struct sockaddr * name,MONKEY_SOCKLENTYPE namelen,int * result)1243 bool CMonkeyPlan::ConnectRule(MONKEY_SOCKTYPE sock,
1244 const struct sockaddr* name,
1245 MONKEY_SOCKLENTYPE namelen,
1246 int* result)
1247 {
1248 short probability_left = 100;
1249 /* We are fine with losing precision. Just need a unique ID*/
1250 MONKEY_SOCKTYPE x_sock = CMonkey::Instance()->GetSockBySocketid(sock);
1251 int res = -1;
1252 for (unsigned int i = 0; i < m_ConnectRules.size(); i++) {
1253 m_ConnectRules[i].AddSocket(x_sock);
1254 unsigned short rule_prob =
1255 m_ConnectRules[i].GetProbability(x_sock);
1256 /* Check if the rule will trigger on this run. If not - we go to the
1257 next rule in plan */
1258 if (m_ConnectRules[i].CheckRun(x_sock, rule_prob, probability_left)) {
1259 /* 'result' is the result of connect() launched in the rule. It can
1260 even be result of original connect() with real parameters.
1261 Or, it can be an error code of a failed fake connect() */
1262 *result = m_ConnectRules[i].Run(sock, name, namelen);
1263 res = 1;
1264 break;
1265 }
1266 /* If this rule did not engage, we go to the next rule, and
1267 and remember to normalize probability of next rule */
1268 probability_left -= rule_prob;
1269 if (probability_left <= 0) {
1270 stringstream ss;
1271 ss << "Probability below zero for write rule in plan " << m_Name
1272 << ". Check config!";
1273 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
1274 NULL, CMonkeyException::e_MonkeyInvalidArgs,
1275 ss.str());
1276 }
1277 }
1278
1279 // If no rules engaged, return 0
1280 if (res == -1)
1281 res = 0;
1282
1283 // Iterate run in all rules
1284 for (unsigned int i = 0; i < m_ConnectRules.size(); i++) {
1285 m_ConnectRules[i].IterateRun(x_sock);
1286 }
1287
1288 return res == 0 ? false : true;
1289 }
1290
1291
PollRule(size_t * n,SOCK * sock,EIO_Status * return_status)1292 bool CMonkeyPlan::PollRule(size_t* n,
1293 SOCK* sock,
1294 EIO_Status* return_status)
1295 {
1296 short probability_left = 100;
1297 int res = -1;
1298 for (unsigned int i = 0; i < m_PollRules.size(); i++) {
1299 m_PollRules[i].AddSocket((*sock)->sock);
1300 unsigned short rule_prob = m_PollRules[i].GetProbability((*sock)->sock);
1301 if (m_PollRules[i].CheckRun((*sock)->sock, rule_prob, probability_left))
1302 {
1303 res = m_PollRules[i].Run(n, sock, return_status) ? 1 : 0;
1304 break;
1305 }
1306 LOG_POST(Error << "[CMonkeyPlan::PollRule] CHAOS MONKEY NOT "
1307 "ENGAGED!!! poll() passed");
1308 /* If this rule did not engage, we go to the next rule, and
1309 and remember to normalize probability of next rule */
1310 probability_left -= rule_prob;
1311 if (probability_left <= 0) {
1312 stringstream ss;
1313 ss << "Probability below zero for write rule in plan " << m_Name
1314 << ". Check config!";
1315 throw CMonkeyException(
1316 CDiagCompileInfo(__FILE__, __LINE__),
1317 NULL, CMonkeyException::e_MonkeyInvalidArgs,
1318 ss.str());
1319 }
1320 }
1321
1322 // If no rules triggered, return 0
1323 if (res == -1) {
1324 res = 0;
1325 *return_status = EIO_Status::eIO_Success;
1326 }
1327
1328 // Iterate run in all rules
1329 for (unsigned int i = 0; i < m_PollRules.size(); i++) {
1330 m_PollRules[i].IterateRun((*sock)->sock);
1331 }
1332
1333 return res == 0 ? false : true;
1334 }
1335
1336
GetProbabilty(void)1337 unsigned short CMonkeyPlan::GetProbabilty(void)
1338 {
1339 return m_Probability;
1340 }
1341
1342
x_ReadPortRange(const string & conf)1343 void CMonkeyPlan::x_ReadPortRange(const string& conf)
1344 {
1345 vector<string> ranges = s_Monkey_Split(conf, ',');
1346 unsigned short from, to;
1347 auto it = ranges.begin();
1348 for ( ; it != ranges.end(); it++ ) {
1349 if (it->find('-') != string::npos) {
1350 vector<string> ports = s_Monkey_Split(*it, '-');
1351 from = static_cast<unsigned short>(NStr::StringToUInt(ports[0]));
1352 to = static_cast<unsigned short>(NStr::StringToUInt(ports[1]));
1353 } else {
1354 from = to = static_cast<unsigned short>(NStr::StringToUInt(*it));
1355 }
1356 /* From*/
1357 m_PortRanges.push_back(from);
1358 /* Until */
1359 m_PortRanges.push_back(to);
1360 }
1361 }
1362
1363
1364 //////////////////////////////////////////////////////////////////////////
1365 // CMonkey
1366 //////////////////////////////////////////////////////////////////////////
1367 CMonkey* CMonkey::sm_Instance = NULL;
1368 FMonkeyHookSwitch CMonkey::sm_HookSwitch = NULL;
1369
1370
CMonkey()1371 CMonkey::CMonkey() : m_Probability(100), m_Enabled(false)
1372 {
1373 if (sm_HookSwitch == NULL) {
1374 throw CMonkeyException(
1375 CDiagCompileInfo(__FILE__, __LINE__),
1376 NULL, CMonkeyException::e_MonkeyInvalidArgs,
1377 "Launch CONNECT_Init() before initializing CMonkey instance");
1378 }
1379 m_TlsToken = new CTls<int>;
1380 m_TlsRandList = new CTls<vector<int> >;
1381 m_TlsRandListPos = new CTls<int>;
1382 srand((unsigned int)time(NULL));
1383 m_Seed = rand();
1384 LOG_POST(Note << "[CMonkey::CMonkey()] Chaos Monkey seed is: " << m_Seed);
1385 ReloadConfig();
1386 }
1387
1388
Instance()1389 CMonkey* CMonkey::Instance()
1390 {
1391 CFastMutexGuard spawn_guard(s_SingletonMutex);
1392
1393 if (sm_Instance == NULL) {
1394 sm_Instance = new CMonkey;
1395 }
1396 return sm_Instance;
1397 }
1398
1399
IsEnabled()1400 bool CMonkey::IsEnabled()
1401 {
1402 return m_Enabled;
1403 }
1404
1405
ReloadConfig(const string & config)1406 void CMonkey::ReloadConfig(const string& config)
1407 {
1408 assert(sm_HookSwitch != NULL);
1409 CFastMutexGuard spawn_guard(s_ConfigMutex);
1410 string rules;
1411 string monkey_section = config.empty() ? s_GetMonkeySection() :
1412 config;
1413 list<string> sections;
1414 m_Plans.clear();
1415 CNcbiRegistry& reg = CNcbiApplication::Instance()->GetConfig();
1416 if (ConnNetInfo_Boolean(reg.Get(kMonkeyMainSect, kEnablField).c_str()) != 1)
1417 {
1418 LOG_POST(Note << "[CMonkey::ReloadConfig] Chaos Monkey is disabled "
1419 << "in [" << kMonkeyMainSect << "]");
1420 m_Enabled = false;
1421 return;
1422 }
1423 string seed = reg.Get(kMonkeyMainSect, kSeedField).c_str();
1424 if (seed != "") {
1425 LOG_POST(Note << "[CMonkey::ReloadConfig] Chaos Monkey seed is set "
1426 << "to " << seed << " in config");
1427 SetSeed(NStr::StringToInt(seed));
1428 }
1429 reg.EnumerateSections(§ions);
1430 /* If the section does not exist */
1431 if (find(sections.begin(), sections.end(), monkey_section)
1432 == sections.end()) {
1433 LOG_POST(Error << "[CMonkey::ReloadConfig] Config section [" <<
1434 monkey_section << "] does not exist, Chaos Monkey "
1435 "is NOT active!");
1436 m_Enabled = false;
1437 return;
1438 }
1439 if (ConnNetInfo_Boolean(reg.Get(monkey_section, kEnablField).c_str()) != 1)
1440 {
1441 LOG_POST(Note << "[CMonkey::ReloadConfig] Chaos Monkey is disabled "
1442 << "in [" << monkey_section << "]");
1443 m_Enabled = false;
1444 return;
1445 }
1446 m_Enabled = true;
1447 LOG_POST(Error << "[CMonkey::ReloadConfig] Chaos Monkey is active!");
1448 string prob_str = reg.Get(monkey_section, "probability");
1449 if (prob_str != "") {
1450 prob_str = NStr::Replace(prob_str, " ", "");
1451 int prob;
1452 try {
1453 if (*prob_str.rbegin() == '%') {
1454 string prob_tmp = prob_str.substr(0, prob_str.length() - 1);
1455 prob = NStr::StringToInt(prob_tmp);
1456 }
1457 else {
1458 prob = (int)(NStr::StringToDouble(prob_str) * 100);
1459 }
1460 if (prob < 0 || prob > 100) {
1461 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
1462 NULL,
1463 CMonkeyException::e_MonkeyInvalidArgs,
1464 "Parameter \"probability\"=" + prob_str
1465 + " for section [" + monkey_section
1466 + "] is not a valid value "
1467 "(valid range is 0%-100% or 0.0-1.0)");
1468 }
1469 m_Probability = (unsigned short)prob;
1470 }
1471 catch (const CStringException&) {
1472 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
1473 NULL, CMonkeyException::e_MonkeyInvalidArgs,
1474 "Parameter \"probability\"=" + prob_str
1475 + " for section [" + monkey_section
1476 + "] could not be parsed");
1477 }
1478 }
1479 // Disable hooks while Monkey initializes
1480 sm_HookSwitch(eMonkeyHookSwitch_Disabled);
1481 unsigned short total_probability = 0;
1482 for (int i = 1; ; ++i) {
1483 string section = monkey_section + "_PLAN" + NStr::IntToString(i);
1484 if (find(sections.begin(), sections.end(), section) ==
1485 sections.end()) {
1486 break;
1487 }
1488 m_Plans.push_back(CMonkeyPlan(section));
1489 total_probability += (*m_Plans.rbegin()).GetProbabilty();
1490 }
1491 /* Check that sum of probabilities for plans is less than 100%, otherwise -
1492 * throw an exception with an easy-to-read message */
1493 if (total_probability > 100) {
1494 stringstream ss;
1495 ss << "Total probability for plans in configuration " <<
1496 monkey_section << " exceeds 100%. Please check that summary " <<
1497 "probability for plans stays under 100% (where the remaining "
1498 "percents go to connections that are not intercepted by any plan)."
1499 "\nTurning Chaos Monkey off";
1500 LOG_POST(Critical << ss.str());
1501 m_Enabled = false;
1502 }
1503 if (m_Enabled) {
1504 s_TimeoutingSocketInit();
1505 }
1506 else {
1507 s_TimeoutingSocketDestroy();
1508 }
1509 sm_HookSwitch(m_Enabled ? eMonkeyHookSwitch_Enabled
1510 : eMonkeyHookSwitch_Disabled);
1511 }
1512
1513
Send(MONKEY_SOCKTYPE sock,const MONKEY_DATATYPE data,MONKEY_LENTYPE size,int flags,SOCK * sock_ptr)1514 MONKEY_RETTYPE CMonkey::Send(MONKEY_SOCKTYPE sock,
1515 const MONKEY_DATATYPE data,
1516 MONKEY_LENTYPE size,
1517 int flags,
1518 SOCK* sock_ptr)
1519 {
1520 string host_fqdn, host_IP;
1521 unsigned short peer_port;
1522 if (m_Enabled) {
1523 x_GetSocketDestinations(sock, &host_fqdn, &host_IP, NULL, &peer_port);
1524 LOG_POST(Note << "[CMonkey::Send] For connection with port "
1525 << peer_port << ", host " << host_IP
1526 << " and hostname " << host_fqdn << ".");
1527 CMonkeyPlan* sock_plan = x_FindPlan(sock, host_fqdn,host_IP,peer_port);
1528
1529 if (sock_plan != NULL) {
1530 MONKEY_RETTYPE bytes_written;
1531 /* Plan may decide to leave connection untouched */
1532 if (sock_plan->WriteRule(sock, data, size, flags, &bytes_written,
1533 sock_ptr) )
1534 return bytes_written;
1535 }
1536 }
1537 return send(sock, (const char*)data, size, flags);
1538 }
1539
1540
Recv(MONKEY_SOCKTYPE sock,MONKEY_DATATYPE buf,MONKEY_LENTYPE size,int flags,SOCK * sock_ptr)1541 MONKEY_RETTYPE CMonkey::Recv(MONKEY_SOCKTYPE sock,
1542 MONKEY_DATATYPE buf,
1543 MONKEY_LENTYPE size,
1544 int flags,
1545 SOCK* sock_ptr)
1546 {
1547 string host_fqdn, host_IP;
1548 unsigned short peer_port;
1549 if (m_Enabled) {
1550 x_GetSocketDestinations(sock, &host_fqdn, &host_IP, NULL, &peer_port);
1551 LOG_POST(Note << "[CMonkey::Recv] For connection with port "
1552 << peer_port << ", host " << host_IP
1553 << " and hostname " << host_fqdn << ".");
1554
1555 CMonkeyPlan* sock_plan = x_FindPlan(sock, host_fqdn, host_IP,peer_port);
1556
1557 if (sock_plan != NULL) {
1558 MONKEY_RETTYPE bytes_read;
1559 if (sock_plan->ReadRule(sock, buf, size, flags, &bytes_read,
1560 sock_ptr))
1561 return bytes_read;
1562 }
1563 }
1564 return recv(sock, buf, size, flags);
1565 }
1566
1567
Connect(MONKEY_SOCKTYPE sock,const struct sockaddr * name,MONKEY_SOCKLENTYPE namelen)1568 int CMonkey::Connect(MONKEY_SOCKTYPE sock,
1569 const struct sockaddr* name,
1570 MONKEY_SOCKLENTYPE namelen)
1571 {
1572 union {
1573 struct sockaddr sa;
1574 struct sockaddr_in in;
1575 #ifdef NCBI_OS_UNIX
1576 struct sockaddr_un un;
1577 #endif /*NCBI_OS_UNIX*/
1578 } addr;
1579 if (m_Enabled) {
1580 addr.sa = *name;
1581 unsigned int host = addr.in.sin_addr.s_addr;
1582 string host_fqdn, host_IP;
1583 unsigned short peer_port = ntohs(addr.in.sin_port);
1584 SFqdnIp&& net_data = x_GetFqdnIp(host);
1585 LOG_POST(Note << "[CMonkey::Connect] For connection with port "
1586 << peer_port << ", host " << host_IP
1587 << " and hostname " << host_fqdn << ".");
1588 CMonkeyPlan* sock_plan = x_FindPlan(CMonkey::GetSockBySocketid(sock),
1589 net_data.fqdn, net_data.ip,
1590 peer_port);
1591 if (sock_plan != NULL) {
1592 int result;
1593 if ( sock_plan->ConnectRule(sock, name, namelen, &result) )
1594 return result;
1595 }
1596 }
1597 return connect(sock, name, namelen);
1598 }
1599
1600
Poll(size_t * n,SSOCK_Poll ** polls,EIO_Status * return_status)1601 bool CMonkey::Poll(size_t* n,
1602 SSOCK_Poll** polls,
1603 EIO_Status* return_status)
1604 {
1605 if (m_Enabled) {
1606 size_t polls_iter = 0;
1607 SSOCK_Poll* new_polls =
1608 static_cast<SSOCK_Poll*>(calloc(*n, sizeof(SSOCK_Poll)));
1609 int polls_count = 0;
1610 while (polls_iter < *n) {
1611 SOCK& sock = (*polls)[polls_iter].sock;
1612 if (sock == NULL) continue;
1613 LOG_POST(Note << "[CMonkey::Poll] For connection with port "
1614 << sock->myport << ", host <empty> and "
1615 "hostname <empty>.");
1616 if (sock != nullptr)
1617 {
1618 CMonkeyPlan* sock_plan = x_FindPlan(sock->id, "", "",
1619 sock->myport);
1620 if (sock_plan == NULL ||
1621 (sock_plan != NULL && !sock_plan->PollRule(n, &sock,
1622 return_status)) )
1623 {
1624 new_polls[polls_count++] = (*polls)[polls_iter];
1625 }
1626 } else { /* something unreadable by Monkey, but still valid */
1627 new_polls[polls_count++] = (*polls)[polls_iter];
1628 }
1629 polls_iter++;
1630 }
1631 *polls = new_polls;
1632 *n = polls_count;
1633 }
1634 return false;
1635 }
1636
1637
Close(MONKEY_SOCKTYPE sock)1638 void CMonkey::Close(MONKEY_SOCKTYPE sock)
1639 {
1640 auto sock_plan = m_KnownSockets.find(sock);
1641 if (sock_plan != m_KnownSockets.end()) {
1642 m_KnownSockets.erase(sock_plan);
1643 }
1644 auto sock_iter = m_SocketMemory.begin();
1645 for ( ; sock_iter != m_SocketMemory.end() ; )
1646 {
1647 if (sock_iter->first == sock)
1648 m_SocketMemory.erase(sock_iter++);
1649 else
1650 ++sock_iter;
1651 }
1652 }
1653
1654
SockHasSocket(SOCK sock,MONKEY_SOCKTYPE socket)1655 void CMonkey::SockHasSocket(SOCK sock, MONKEY_SOCKTYPE socket)
1656 {
1657 m_SocketMemory[socket] = sock;
1658 }
1659
1660
GetSockBySocketid(MONKEY_SOCKTYPE socket)1661 MONKEY_SOCKTYPE CMonkey::GetSockBySocketid(MONKEY_SOCKTYPE socket)
1662 {
1663 union SSockSocket
1664 {
1665 SOCK sock;
1666 MONKEY_SOCKTYPE socket;
1667 };
1668 SSockSocket sock_socket;
1669 sock_socket.sock = m_SocketMemory[socket];
1670 return sock_socket.socket;
1671 }
1672
1673
MonkeyHookSwitchSet(FMonkeyHookSwitch hook_switch_func)1674 void CMonkey::MonkeyHookSwitchSet(FMonkeyHookSwitch hook_switch_func)
1675 {
1676 sm_HookSwitch = hook_switch_func;
1677 }
1678
1679
1680 /** Return plan for the socket, new or already assigned one. If the socket
1681 * is ignored by Chaos Monkey, NULL is returned */
x_FindPlan(MONKEY_SOCKTYPE sock,const string & hostname,const string & host_IP,unsigned short port)1682 CMonkeyPlan* CMonkey::x_FindPlan(MONKEY_SOCKTYPE sock, const string& hostname,
1683 const string& host_IP, unsigned short port)
1684 {
1685 CFastMutexGuard spawn_guard(s_KnownConnMutex);
1686 auto sock_plan = m_KnownSockets.find(sock);
1687 if (sock_plan != m_KnownSockets.end()) {
1688 return sock_plan->second;
1689 }
1690 /* Plan was not found. First roll the dice to know if Monkey will process
1691 * current socket */
1692 int rand_val = CMonkey::Instance()->GetRand(Key()) % 100;
1693 LOG_POST(Note << "[CMonkey::x_FindPlan] Checking if connection "
1694 "with port " << port << ", host " << host_IP
1695 << " and hostname " << hostname << " will be "
1696 << "intercepted by Chaos Monkey. Random value is "
1697 << rand_val << ", probability threshold is "
1698 << m_Probability);
1699 LOG_POST(Note << "[CMonkey::x_FindPlan] The connection will "
1700 << ((rand_val >= m_Probability) ? "NOT " : "")
1701 << "be processed.");
1702 if (rand_val >= m_Probability) {
1703 return NULL;
1704 }
1705 /* Now we can find a plan */
1706 bool match_found = false;
1707 unsigned short probability_left = 100; /* If we tried to match a plan with
1708 n% probability with no luck,
1709 next plan only has (100-n)%
1710 fraction of connections to match
1711 against, so if its probability to
1712 all connections is m%, its
1713 probability to connections left
1714 is m/(100-n)*100%. To count this,
1715 we need probability_left */
1716 for (unsigned int i = 0; i < m_Plans.size() && !match_found; i++) {
1717 /* Match includes probability of plan*/
1718 if (m_Plans[i].Match(host_IP, port, hostname, probability_left)) {
1719 // 3. If found plan - use it and assign to this socket
1720 m_KnownSockets[sock] = &m_Plans[i];
1721 return m_KnownSockets[sock];
1722 }
1723 probability_left -= m_Plans[i].GetProbabilty();
1724 }
1725 /* If no plan triggered, then this socket will be always ignored */
1726 m_KnownSockets[sock] = NULL;
1727 return NULL;
1728 }
1729
1730
x_GetFqdnIp(unsigned int host)1731 CMonkey::SFqdnIp CMonkey::x_GetFqdnIp(unsigned int host)
1732 {
1733 CFastMutexGuard guard(s_NetworkDataCacheMutex);
1734 auto iter = m_NetworkDataCache.find(host);
1735 if (iter != m_NetworkDataCache.end())
1736 return iter->second;
1737 string host_fqdn = CSocketAPI::gethostbyaddr(host);
1738 string host_IP = CSocketAPI::HostPortToString(host, 0);
1739 return m_NetworkDataCache[host] = SFqdnIp(host_fqdn, host_IP);
1740 }
1741
1742
RegisterThread(int token)1743 bool CMonkey::RegisterThread(int token)
1744 {
1745 if (!m_Enabled) {
1746 ERR_POST(Error << "[CMonkey::RegisterThread] Chaos Monkey is "
1747 << "disabled, the thread with token "
1748 << token << " was not registered");
1749 return false;
1750 }
1751 CFastMutexGuard guard(s_SeedLogConfigMutex);
1752 LOG_POST(Note << "[CMonkey::RegisterThread] Registering thread with "
1753 << "token " << token);
1754
1755 stringstream ss;
1756 ss << "[CMonkey::RegisterThread] Token " << token
1757 << " has been already registered in CMonkey and cannot be used again";
1758 if (m_RegisteredTokens.find(token) != m_RegisteredTokens.end()) {
1759 throw CMonkeyException(
1760 CDiagCompileInfo(__FILE__, __LINE__),
1761 NULL, CMonkeyException::e_MonkeyInvalidArgs,
1762 ss.str());
1763 }
1764 m_RegisteredTokens.insert(token);
1765
1766 /* Remember token */
1767 m_TlsToken->SetValue(new int, s_TlsCleanup<int>);
1768 *m_TlsToken->GetValue() = token;
1769
1770 vector<int>* rand_list = new vector<int>();
1771 srand(m_Seed + token);
1772 stringstream rand_list_str;
1773 for (unsigned int i = 0; i < kRandCount; ++i) {
1774 rand_list->push_back(rand());
1775 rand_list_str << *rand_list->rbegin() << " ";
1776 }
1777 LOG_POST(Note << "[CMonkey::RegisterThread] Random list for token "
1778 << token << " is " << rand_list_str.str());
1779 m_TlsRandList->SetValue(rand_list, s_TlsCleanup< vector<int> >);
1780
1781 m_TlsRandListPos->SetValue(new int, s_TlsCleanup<int>);
1782 *m_TlsRandListPos->GetValue() = 0;
1783
1784 return true;
1785 }
1786
1787
GetSeed()1788 int CMonkey::GetSeed()
1789 {
1790 /* save m_seedlog to file */
1791 return m_Seed;
1792 }
1793
1794
SetSeed(int seed)1795 void CMonkey::SetSeed(int seed)
1796 {
1797 /* load m_seedlog from file */
1798 m_Seed = seed;
1799 LOG_POST(Info << "[CMonkey::SetSeed] Chaos Monkey seed was manually "
1800 "changed to: " << m_Seed);
1801 }
1802
1803
GetRand(const CMonkeySeedKey &)1804 int CMonkey::GetRand(const CMonkeySeedKey& /* key */)
1805 {
1806 if (m_TlsToken->GetValue() == NULL) {
1807 return rand();
1808 }
1809 int& list_pos = *m_TlsRandListPos->GetValue();
1810 if (++list_pos == kRandCount) {
1811 list_pos = 0;
1812 }
1813 int next_list_pos = (list_pos + 1 == kRandCount) ? 0 : list_pos + 1;
1814 LOG_POST(Note << "[CMonkey::GetRand] Getting random value "
1815 << (*m_TlsRandList->GetValue())[list_pos]
1816 << " for thread " << *m_TlsToken->GetValue()
1817 << ". Next random value is "
1818 << (*m_TlsRandList->GetValue())[next_list_pos]);
1819 return (*m_TlsRandList->GetValue())[list_pos];
1820 }
1821
1822
1823 END_NCBI_SCOPE
1824
1825 #endif /* #ifdef NCBI_MONKEY */
1826