• Main Page
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

yapet/pwgen/rng.cc

Go to the documentation of this file.
00001 // $Id: rng.cc 3343 2010-09-17 18:36:31Z java $
00002 //
00003 // Copyright (C) 2009-2010  Rafael Ostertag
00004 //
00005 // This file is part of YAPET.
00006 //
00007 // YAPET is free software: you can redistribute it and/or modify it under the
00008 // terms of the GNU General Public License as published by the Free Software
00009 // Foundation, either version 3 of the License, or (at your option) any later
00010 // version.
00011 //
00012 // YAPET is distributed in the hope that it will be useful, but WITHOUT ANY
00013 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00014 // FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
00015 // details.
00016 //
00017 // You should have received a copy of the GNU General Public License along with
00018 // YAPET.  If not, see <http://www.gnu.org/licenses/>.
00019 //
00020 
00021 #ifdef HAVE_CONFIG_H
00022 # include <config.h>
00023 #endif
00024 
00025 #ifdef HAVE_ERRNO_H
00026 # include <errno.h>
00027 #endif
00028 
00029 #ifdef HAVE_UNISTD_H
00030 # include <unistd.h>
00031 #endif
00032 
00033 #ifdef HAVE_SYS_TYPES_H
00034 # include <sys/types.h>
00035 #endif
00036 
00037 #ifdef HAVE_SYS_STAT_H
00038 # include <sys/stat.h>
00039 #endif
00040 
00041 #ifdef HAVE_FCNTL_H
00042 # include <fcntl.h>
00043 #endif
00044 
00045 #ifdef HAVE_STDIO_H
00046 # include <stdio.h>
00047 #endif
00048 
00049 #ifdef HAVE_STDLIB_H
00050 # include <stdlib.h>
00051 #endif
00052 
00053 #ifdef HAVE_STRING_H
00054 # include <string.h>
00055 #endif
00056 
00057 #ifdef HAVE_ASSERT_H
00058 # include <assert.h>
00059 #endif
00060 
00061 #include "../../intl.h"
00062 
00063 #include "pwgenexception.h"
00064 #include "rng.h"
00065 
00066 
00067 using namespace YAPET::PWGEN;
00068 
00069 int RNG::rng_available = 0;
00070 
00071 void
00072 RNG::check_availability() throw (PWGenException) {
00073     if (access ("/dev/random", R_OK) == 0)
00074         rng_available |= DEVRANDOM;
00075 
00076     if (access ("/dev/urandom", R_OK) == 0 )
00077         rng_available |= DEVURANDOM;
00078 
00079 #ifdef HAVE_LRAND48
00080     rng_available |= LRAND48;
00081 #endif
00082 #ifdef HAVE_RAND
00083     rng_available |= RAND;
00084 #endif
00085     assert (rng_available != 0);
00086 
00087     if (rng_available == 0)
00088         throw PWGenNoRNGException();
00089 }
00090 
00091 int
00092 RNG::getAvailableRNGs() {
00093     check_availability();
00094     return rng_available;
00095 }
00096 
00104 void
00105 RNG::init_rng (RNGENGINE request) throw (PWGenException) {
00106     assert (rng_available != 0);
00107 
00108     if (rng_available == 0)
00109         throw PWGenException (_ ("Unable to initialize RNG when none is available") );
00110 
00111     if (! (rng_available & request) )
00112         throw PWGenRNGNotAvailable();
00113 
00114     switch (request) {
00115         case DEVRANDOM:
00116             fd = open ("/dev/random", O_RDONLY);
00117 
00118             if ( fd < 0 )
00119                 throw PWGenException (_ ("Unable to open /dev/random") );
00120 
00121             break;
00122         case DEVURANDOM:
00123             fd = open ("/dev/urandom", O_RDONLY);
00124 
00125             if ( fd < 0 )
00126                 throw PWGenException (_ ("Unable to open /dev/urandom") );
00127 
00128             break;
00129         case LRAND48:
00130 #if defined(HAVE_SRAND48) && defined(HAVE_TIME)
00131             srand48 (time (NULL) );
00132 #endif
00133             break;
00134         case RAND:
00135 #if defined(HAVE_RAND) && defined(HAVE_TIME)
00136             srand (time (NULL) );
00137 #endif
00138             break;
00139         case AUTO:
00140             assert (0);
00141             throw PWGenException (_ ("Unexpected RNG Engine (AUTO)") );
00142             // To make the compiler (gcc -pedantic) happy
00143         case NONE:
00144             throw PWGenException (_ ("The requested RNG Engine (NONE) is invalid.") );
00145     }
00146 
00147     rng_used = request;
00148     rng_initialized = true;
00149 }
00150 
00160 size_t
00161 RNG::devrandom (size_t ceil) throw (PWGenException) {
00162     assert (rng_initialized);
00163     assert (rng_used == DEVRANDOM || rng_used == DEVURANDOM);
00164     assert (fd > -1);
00165     size_t buff;
00166     // This is an attempt to circumvent short reads appearing on some lx systems.
00167     //
00168     // Code courtesy of Richard W. Stevens. Thanks man!
00169     size_t nleft;
00170     ssize_t nread;
00171     size_t *ptr;
00172 
00173     ptr = &buff;
00174     nleft = sizeof(size_t);
00175     while( nleft > 0) {
00176     errno = 0;
00177     if ( (nread = read (fd, ptr, nleft )) < 0) {
00178         // Error
00179         switch (errno) {
00180         case EAGAIN:
00181         case EINTR:
00182             // Just ignore and try again
00183             break;
00184         default: {
00185             char errmsg[1024];
00186             snprintf(errmsg, 1024, "%s (%s)", _ ("Read to few bytes on /dev/[u]random."),
00187                  strerror(errno));
00188             throw PWGenException ( errmsg );
00189         }
00190         };
00191     } else {
00192         if (nread == 0) {
00193         // EOF
00194         break;
00195         }
00196         nleft -= nread;
00197         ptr += nread;
00198     }
00199     }
00200 
00201     if (buff > ceil)
00202         return buff % ceil;
00203 
00204     return buff;
00205 }
00206 
00207 size_t
00208 RNG::_lrand48 (size_t ceil) throw() {
00209     assert (rng_initialized);
00210     assert (rng_used == LRAND48);
00211 #ifdef HAVE_LRAND48
00212     long val = lrand48();
00213 
00214     if ( (size_t) val > ceil)
00215         return val % ceil;
00216 
00217     return (size_t) val;
00218 #else
00219     assert (0);
00220     throw PWGenRNGNotAvailable (_ ("lrand48() not available on system") );
00221     // To make compiler happy
00222     return 0;
00223 #endif
00224 }
00225 
00226 size_t
00227 RNG::_rand (size_t ceil) throw() {
00228     assert (rng_initialized);
00229     assert (rng_used == RAND);
00230     assert (RAND_MAX >= ceil);
00231 #ifdef HAVE_RAND
00232     int val = rand();
00233 
00234     if ( (size_t) val > ceil)
00235         return val % ceil;
00236 
00237     return (size_t) val;
00238 #else
00239     assert (0);
00240     throw PWGenRNGNotAvailable (_ ("rand() not available on system") );
00241     // To make compiler happy
00242     return 0;
00243 #endif
00244 }
00245 
00262 RNG::RNG (RNGENGINE request) throw (PWGenException) : fd (-1),
00263         rng_initialized (false),
00264         rng_used (NONE) {
00265     check_availability();
00266 
00267     if (request != AUTO) {
00268         init_rng (request);
00269     } else {
00270         assert (rng_available != 0);
00271 
00272     // Since version 0.6, /dev/urandom is the default rng used.
00273         if (rng_available & DEVURANDOM) {
00274             init_rng (DEVURANDOM);
00275             return;
00276         }
00277 
00278         if (rng_available & DEVRANDOM) {
00279             init_rng (DEVRANDOM);
00280             return;
00281         }
00282 
00283         if (rng_available & LRAND48) {
00284             init_rng (LRAND48);
00285             return;
00286         }
00287 
00288         if (rng_available & RAND) {
00289             init_rng (RAND);
00290             return;
00291         }
00292     }
00293 }
00294 
00295 //
00296 // *** Copy Constructor
00297 //
00298 RNG::RNG (const RNG& r) throw (PWGenException) {
00299     assert (r.rng_initialized);
00300 
00301     // Code below takes care of the file descriptor
00302     if ( (r.rng_used == DEVRANDOM) ||
00303             (r.rng_used == DEVURANDOM) ) {
00304         assert (r.fd > -1);
00305         fd = dup (r.fd);
00306 
00307         if (fd < 0)
00308             throw PWGenException ("Unable to duplicate file descriptor");
00309     } else {
00310         assert (r.fd == -1);
00311         fd = r.fd;
00312     }
00313 
00314     // From here on we may more or less simply copy values
00315     rng_initialized = r.rng_initialized;
00316     rng_used = r.rng_used;
00317     rng_available = r.rng_available;
00318 
00319     if (rng_used == LRAND48)
00320         init_rng (LRAND48);
00321 
00322     if (rng_used == RAND)
00323         init_rng (RAND);
00324 }
00325 
00326 RNG::~RNG() throw() {
00327     if (rng_used == DEVRANDOM ||
00328             rng_used == DEVURANDOM) {
00329         assert (fd > 0);
00330         close (fd);
00331     } else {
00332         assert (fd == -1);
00333     }
00334 }
00335 
00336 size_t
00337 RNG::getRandomNumber (size_t ceil) throw (PWGenException) {
00338     assert(ceil > 0);
00339     if (ceil == 0) return 0;
00340     assert (rng_initialized == true);
00341 
00342     switch (rng_used) {
00343         case DEVRANDOM:
00344         case DEVURANDOM:
00345             return devrandom (ceil);
00346         case LRAND48:
00347             return _lrand48 (ceil);
00348         case RAND:
00349             return _rand (ceil);
00350             // To make the compiler (gcc -pedantic) happy
00351         case AUTO:
00352             assert (0);
00353             throw PWGenException (_ ("Unexpected RNG Engine (AUTO)") );
00354         case NONE:
00355             throw PWGenException (_ ("The requested RNG Engine (NONE) is invalid.") );
00356     }
00357 
00358     // To make the compiler even more happy
00359     return 0;
00360 }
00361 
00362 const RNG&
00363 RNG::operator= (const RNG & r) throw() {
00364     if (&r == this) return *this;
00365 
00366     assert (r.rng_initialized);
00367 
00368     // Make sure we close the current fd if used
00369     if (rng_used == DEVRANDOM ||
00370             rng_used == DEVURANDOM) {
00371         assert (fd > -1);
00372         close (fd);
00373         fd = -1;
00374     } else {
00375         assert (fd == -1);
00376     }
00377 
00378     // Code below takes care of the file descriptor from r
00379     if ( (r.rng_used == DEVRANDOM) ||
00380             (r.rng_used == DEVURANDOM) ) {
00381         assert (r.fd > -1);
00382         fd = dup (r.fd);
00383 
00384         if (fd < 0)
00385             throw PWGenException ("Unable to duplicate file descriptor");
00386     } else {
00387         assert (r.fd == -1);
00388         fd = r.fd;
00389     }
00390 
00391     // From here on we may more or less simply copy values
00392     rng_initialized = r.rng_initialized;
00393     rng_used = r.rng_used;
00394     rng_available = r.rng_available;
00395 
00396     if (rng_used == LRAND48)
00397         init_rng (LRAND48);
00398 
00399     if (rng_used == RAND)
00400         init_rng (RAND);
00401 
00402     return *this;
00403 }

Generated on Sun Sep 19 2010 15:37:14 for YAPET by  doxygen 1.7.1