reSIProcate/rutil  9694
Public Types | Static Public Member Functions | Static Private Attributes
resip::Random Class Reference

A static class that wraps the random-number generation code of your platform. More...

#include <Random.hxx>

Collaboration diagram for resip::Random:
Collaboration graph
[legend]

List of all members.

Public Types

enum  { maxLength = 512 }

Static Public Member Functions

static unsigned getSimpleSeed ()
 Key goal is to make sure that each thread has distinct seed.
static void initialize ()
static Data getRandom (unsigned int numBytes)
static Data getRandomHex (unsigned int numBytes)
static Data getRandomBase64 (unsigned int numBytes)
static Data getCryptoRandom (unsigned int numBytes)
static Data getCryptoRandomHex (unsigned int numBytes)
static Data getCryptoRandomBase64 (unsigned int numBytes)
static void getCryptoRandom (unsigned char *buf, unsigned int numBytes)
static Data getVersion4UuidUrn ()
 Returns a version 4 (random) UUID as defined in RFC 4122.
static int getRandom ()
 Returns a postive integer (31 bits) of randomness.
static int getCryptoRandom ()
static const char * getImplName ()

Static Private Attributes

static Mutex mMutex
static bool mIsInitialized = false

Detailed Description

A static class that wraps the random-number generation code of your platform.

Definition at line 49 of file Random.hxx.


Member Enumeration Documentation

anonymous enum
Enumerator:
maxLength 

Definition at line 55 of file Random.hxx.

{maxLength = 512};

Member Function Documentation

Data Random::getCryptoRandom ( unsigned int  numBytes) [static]

Definition at line 355 of file Random.cxx.

References getCryptoRandom(), and resip::Data::Take.

{
   unsigned char* buf = new unsigned char[len];
   getCryptoRandom(buf, len); // USE_SSL check is in here
   return Data(Data::Take, (char*)buf, len);
}

Here is the call graph for this function:

void Random::getCryptoRandom ( unsigned char *  buf,
unsigned int  numBytes 
) [static]

Definition at line 444 of file Random.cxx.

References resip::Data::data(), ErrLog, getRandom(), initialize(), and maxLength.

{
   assert(numBytes < Random::maxLength+1);

#if USE_OPENSSL
   initialize();
   int e = RAND_bytes( (unsigned char*)buf , numBytes );
   if ( e < 0 )
   {
      // error of some type - likely not enough rendomness to dod this 
      long err = ERR_get_error();
      
      char buf[1024];
      ERR_error_string_n(err,buf,sizeof(buf));
      
      ErrLog( << buf );
      assert(0);
   }
#else
   // !bwc! Should optimize this.
   Data temp=Random::getRandom(numBytes);
   memcpy(buf, temp.data(), numBytes);
#endif
}

Here is the call graph for this function:

int Random::getCryptoRandom ( ) [static]

Definition at line 311 of file Random.cxx.

References ErrLog, getRandom(), and initialize().

Referenced by getCryptoRandom(), getCryptoRandomBase64(), getCryptoRandomHex(), and getVersion4UuidUrn().

{
   initialize();

#if USE_OPENSSL
   int ret;
   int e = RAND_bytes( (unsigned char*)&ret , sizeof(ret) );
   if ( e < 0 )
   {
      // error of some type - likely not enough rendomness to dod this 
      long err = ERR_get_error();
      
      char buf[1024];
      ERR_error_string_n(err,buf,sizeof(buf));
      
      ErrLog( << buf );
      assert(0);
   }
   return ret;
#else
   return getRandom();
#endif
}

Here is the call graph for this function:

Data Random::getCryptoRandomBase64 ( unsigned int  numBytes) [static]

Definition at line 381 of file Random.cxx.

References getCryptoRandom().

{
   return Random::getCryptoRandom(numBytes).base64encode();
}

Here is the call graph for this function:

Data Random::getCryptoRandomHex ( unsigned int  numBytes) [static]

Definition at line 375 of file Random.cxx.

References getCryptoRandom().

Referenced by getVersion4UuidUrn().

{
   return Random::getCryptoRandom(numBytes).hex();
}

Here is the call graph for this function:

const char * Random::getImplName ( ) [static]

Definition at line 69 of file Random.cxx.

{
#ifdef WIN32
#if defined(RESIP_RANDOM_WIN32_RTL)
   return "win32_rtl";
#else
   return "win32_rand";
#endif
#else // WIN32
#if defined(RESIP_RANDOM_THREAD_LOCAL)
   return "posix_thread_local";
#elif defined(RESIP_RANDOM_THREAD_MUTEX)
   return "posix_thread_mutex";
#else
   return "posix_random";
#endif
#endif // not WIN32
}
Data Random::getRandom ( unsigned int  numBytes) [static]

Definition at line 336 of file Random.cxx.

References getRandom(), initialize(), and maxLength.

{
   initialize();
   assert(len < Random::maxLength+1);
   
   union 
   {
         char cbuf[Random::maxLength+1];
         unsigned int  ibuf[(Random::maxLength+1)/sizeof(int)];
   };
   
   for (unsigned int count=0; count<(len+sizeof(int)-1)/sizeof(int); ++count)
   {
      ibuf[count] = Random::getRandom();
   }
   return Data(cbuf, len);
}

Here is the call graph for this function:

int Random::getRandom ( ) [static]

Returns a postive integer (31 bits) of randomness.

Implementation is platform dependent.

Definition at line 244 of file Random.cxx.

References getSimpleSeed(), initialize(), mMutex, RANDOM_STATE_SIZE, resip::ThreadIf::tlsGetValue(), and resip::ThreadIf::tlsSetValue().

Referenced by getCryptoRandom(), getRandom(), getRandomBase64(), resip::ResipClock::getRandomFutureTimeMs(), and getRandomHex().

{
   initialize();

#ifdef WIN32

   int ret = 0;

#ifdef RESIP_RANDOM_WIN32_RTL
   // see comment in initialize()
   if (Random::RtlGenRandom)
   {
      unsigned long buff[1];
      ULONG ulCbBuff = sizeof(buff);
      if (Random::RtlGenRandom(buff,ulCbBuff))
      {
         // .kw. all other impls here return positive number, so do the same...
         ret = buff[0] & (~(1<<31));
         return ret;
      }
   }
   // fallback to using rand() if this is a Windows version previous to XP
#endif  // RESIP_RANDOM_WIN32_RTL
   {
      // rand() returns [0,RAND_MAX], which on Windows is 15 bits and positive
      // code below gets 30bits of randomness; with bit31 and bit15
      // always zero; result is always positive
      assert( RAND_MAX == 0x7fff );
      // WATCHOUT: on Linux, rand() returns 31bits, and assert above will fail
      int r1 = rand();
      int r2 = rand();
      ret = (r1<<16) + r2;
   }

   return ret;
#else   // WIN32

#if defined(RESIP_RANDOM_THREAD_LOCAL)
   struct random_data *buf = (struct random_data*) ThreadIf::tlsGetValue(sRandomStateKey);
   if ( buf==NULL ) {
      size_t sz = sizeof(*buf)+RANDOM_STATE_SIZE;
      buf = (struct random_data*) ::malloc(sz);
      memset( buf, 0, sz);      // .kw. strange segfaults without this
      unsigned seed = getSimpleSeed();
      initstate_r(seed, ((char*)buf)+sizeof(*buf), RANDOM_STATE_SIZE, buf);
      ThreadIf::tlsSetValue(sRandomStateKey, buf);
   }
   int32_t ret;
   random_r(buf, &ret);
   return ret;
#elif defined(RESIP_RANDOM_THREAD_MUTEX)
   int32_t ret;
   {
      Lock statelock(mMutex);
      random_r(sRandomState, &ret);
   }
   return ret;
#else
   // random returns [0,RAN_MAX]. On Linux, this is 31 bits and positive.
   // On some platforms it might be on 15 bits, and will need to do something.
   // assert( RAND_MAX == ((1<<31)-1) );  // ?slg? commented out assert since, RAND_MAX is not used in random(), it applies to rand() only
   return random(); 
#endif  // THREAD_LOCAL
#endif // WIN32
}

Here is the call graph for this function:

Data Random::getRandomBase64 ( unsigned int  numBytes) [static]

Definition at line 369 of file Random.cxx.

References getRandom().

{
   return Random::getRandom(numBytes).base64encode();
}

Here is the call graph for this function:

Data Random::getRandomHex ( unsigned int  numBytes) [static]

Definition at line 363 of file Random.cxx.

References getRandom().

Referenced by main().

{
   return Random::getRandom(numBytes).hex();
}

Here is the call graph for this function:

unsigned Random::getSimpleSeed ( ) [static]

Key goal is to make sure that each thread has distinct seed.

Definition at line 92 of file Random.cxx.

References resip::ResipClock::getTimeMicroSec(), resip::Data::hash(), and resip::ThreadIf::selfId().

Referenced by getRandom(), and initialize().

{
   // !cj! need to find a better way - use pentium random commands?
   Data buffer;
   {
      DataStream strm(buffer);
#ifdef WIN32
      strm << GetTickCount() << ":";
      strm << GetCurrentProcessId() << ":";
      strm << GetCurrentThreadId();
#else
      // .kw. previously just used the lower 32bits of getTimeMs()
      strm << ResipClock::getTimeMicroSec() << ":";
      strm << getpid();
#if defined(RESIP_RANDOM_THREAD_LOCAL)
      strm << ":" << ThreadIf::selfId();
#endif
#endif
   }
   return (unsigned int)buffer.hash();
}

Here is the call graph for this function:

Data Random::getVersion4UuidUrn ( ) [static]

Returns a version 4 (random) UUID as defined in RFC 4122.

Todo:
This is something of a suboptimal hack. Ideally, we would encapsulate UUID as its own class, with options to create any of versions 1 through 5 (and Nil UUIDs). This class would have various access methods to pull the result out as a bitstring, as raw data, as a URN, etc. For what I need to do right now, however, version 4 UUIDs get me where I need to go. <abr>

Definition at line 417 of file Random.cxx.

References getCryptoRandom(), getCryptoRandomHex(), and resip::Data::hex().

{
  Data urn ("urn:uuid:");
  urn += getCryptoRandomHex(4); // time-low
  urn += "-";
  urn += getCryptoRandomHex(2); // time-mid
  urn += "-";

  Data time_hi_and_version = Random::getCryptoRandom(2);
  time_hi_and_version[0] &= 0x0f;
  time_hi_and_version[0] |= 0x40;
  urn += time_hi_and_version.hex();

  urn += "-";

  Data clock_seq_hi_and_reserved = Random::getCryptoRandom(1);
  clock_seq_hi_and_reserved[0] &= 0x3f;
  clock_seq_hi_and_reserved[0] |= 0x40;
  urn += clock_seq_hi_and_reserved.hex();

  urn += getCryptoRandomHex(1); // clock-seq-low
  urn += "-";
  urn += getCryptoRandomHex(6); // node
  return urn;
}

Here is the call graph for this function:

void Random::initialize ( ) [static]

ah! blocks if /dev/random on embedded sys

Definition at line 115 of file Random.cxx.

References ErrLog, getSimpleSeed(), mIsInitialized, mMutex, RANDOM_STATE_SIZE, resip::Timer::setupTimeOffsets(), resip::ThreadIf::tlsKeyCreate(), and WarningLog.

Referenced by getCryptoRandom(), and getRandom().

{  
#ifdef WIN32
//#if defined(USE_SSL)
#if 0 //!dcm! - this shouldn't be per thread for win32, and this is slow. Going
      //to re-work openssl initialization
   if ( !Random::mIsInitialized)
   {
      Lock lock(mMutex);
      if (!Random::mIsInitialized)
      {
         mIsInitialized = true;
         RAND_screen ();
      }
   }
#else      
   if (!Random::mInitializer.isInitialized())
   {
      Lock lock(mMutex);      
      if (!Random::mInitializer.isInitialized())
      {
         Random::mInitializer.setInitialized();

         unsigned seed = getSimpleSeed();
         srand(seed);

#ifdef RESIP_RANDOM_WIN32_RTL
         // .jjg. from http://blogs.msdn.com/michael_howard/archive/2005/01/14/353379.aspx
         // srand(..) and rand() have proven to be insufficient sources of randomness,
         // leading to transaction id collisions in resip.
         // SystemFunction036 maps to RtlGenRandom, which is used by rand_s() (which is available
         // only with the VC 8.0 runtime or later) and is the Microsoft-recommended way of getting
         // a random number. This code allows that functionality to be accessed even from VC 7.1.
         // However, SystemFunction036 only exists in Windows XP and later, so we may need to fallback
         // to the old method using rand().
         HMODULE hLib = LoadLibrary("ADVAPI32.DLL");
         if (hLib)
         {
            Random::RtlGenRandom =
               (BOOLEAN (APIENTRY *)(void*,ULONG))GetProcAddress(hLib,"SystemFunction036");

            if (!Random::RtlGenRandom)
            {
               WarningLog(<< "Using srand(..) and rand() for random numbers");
            }
         }
#endif   // RESIP_RANDOM_WIN32_RTL

         mIsInitialized = true;

      }
   }
#endif  // not dead code

#else   // WIN32
   // ?dcm? -- OpenSSL will transparently initialize PRNG if /dev/urandom is
   // present. In any case, will move into OpenSSLInit
   if ( !Random::mIsInitialized)
   {
      Lock lock(mMutex);
      if (!Random::mIsInitialized)
      {
         mIsInitialized = true;
         Timer::setupTimeOffsets();

         unsigned seed = getSimpleSeed();

#if defined(RESIP_RANDOM_THREAD_LOCAL)
         ThreadIf::tlsKeyCreate(sRandomStateKey, ::free);
#elif defined(RESIP_RANDOM_THREAD_MUTEX)
         struct random_data *buf;
         size_t sz = sizeof(*buf)+RANDOM_STATE_SIZE;
         buf = (struct random_data*) ::malloc(sz);
         memset( buf, 0, sz);      // .kw. strange segfaults without this
         initstate_r(seed, ((char*)buf)+sizeof(*buf), RANDOM_STATE_SIZE, buf);
         sRandomState = buf;
#else
         srandom(seed);
#endif


         int fd = open("/dev/urandom", O_RDONLY);
         // !ah! blocks on embedded devices -- not enough entropy.
         if ( fd != -1 )
         {
            int s = read( fd,&seed,sizeof(seed) ); 

            if ( s != sizeof(seed) )
            {
               ErrLog( << "System is short of randomness" ); // !ah! never prints
            }
         }
         else
         {
            ErrLog( << "Could not open /dev/urandom" );
         }

#if defined(USE_SSL)
         if (fd == -1 )
         {
            // really bad sign - /dev/random does not exist so need to intialize
            // OpenSSL some other way

            // !cj! need to fix         assert(0);
         }
         else
         {
            char buf[1024/8]; // size is number byes used for OpenSSL init 

            int s = read( fd,&buf,sizeof(buf) );

            if ( s != sizeof(buf) )
            {
               ErrLog( << "System is short of randomness" );
            }
         
            RAND_add(buf,sizeof(buf),double(s*8));
         }
#endif   // SSL
         if (fd != -1 )
         {
            ::close(fd);
         }
      }
   }
#endif  // not WIN32
}

Here is the call graph for this function:


Member Data Documentation

bool Random::mIsInitialized = false [static, private]

Definition at line 91 of file Random.hxx.

Referenced by initialize().

Mutex Random::mMutex [static, private]

Definition at line 90 of file Random.hxx.

Referenced by getRandom(), and initialize().


The documentation for this class was generated from the following files: