| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include "random.hpp" |
| |
| #include "cassandra.h" |
| #include "driver_config.hpp" |
| #include "logger.hpp" |
| #include "scoped_lock.hpp" |
| |
| #if defined(_WIN32) |
| #ifndef _WINSOCKAPI_ |
| #define _WINSOCKAPI_ |
| #endif |
| #include <WinCrypt.h> |
| #include <Windows.h> |
| #else |
| #if defined(HAVE_GETRANDOM) |
| #include <linux/random.h> |
| #include <syscall.h> |
| #endif |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/uio.h> |
| #include <unistd.h> |
| #endif |
| |
| namespace datastax { namespace internal { |
| |
| Random::Random() |
| // Use high resolution time if we can't get a real random seed |
| : rng_(get_random_seed(uv_hrtime())) { |
| uv_mutex_init(&mutex_); |
| } |
| |
| Random::~Random() { uv_mutex_destroy(&mutex_); } |
| |
| uint64_t Random::next(uint64_t max) { |
| ScopedMutex l(&mutex_); |
| |
| if (max == 0) { |
| return 0; |
| } |
| |
| const uint64_t limit = CASS_UINT64_MAX - CASS_UINT64_MAX % max; |
| uint64_t r; |
| do { |
| r = rng_(); |
| } while (r >= limit); |
| return r % max; |
| } |
| |
| #if defined(_WIN32) |
| |
| uint64_t get_random_seed(uint64_t seed) { |
| HCRYPTPROV provider; |
| |
| if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, |
| CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { |
| LOG_CRITICAL("Unable to aqcuire cryptographic provider: 0x%x", GetLastError()); |
| return seed; |
| } |
| |
| if (!CryptGenRandom(provider, sizeof(seed), (BYTE*)&seed)) { |
| LOG_CRITICAL("An error occurred attempting to generate random data: 0x%x", GetLastError()); |
| return seed; |
| } |
| |
| CryptReleaseContext(provider, 0); |
| |
| return seed; |
| } |
| |
| #else |
| |
| #define STRERROR_BUFSIZE_ 256 |
| |
| #if defined(__APPLE__) || defined(__FreeBSD__) || !defined(_GNU_SOURCE) || !defined(__GLIBC__) |
| #define STRERROR_R_(errno, buf, bufsize) (strerror_r(errno, buf, bufsize), buf) |
| #else |
| #define STRERROR_R_(errno, buf, bufsize) strerror_r(errno, buf, bufsize) |
| #endif |
| |
| uint64_t get_random_seed(uint64_t seed) { |
| #if defined(HAVE_ARC4RANDOM) |
| arc4random_buf(&seed, sizeof(seed)); |
| #else |
| static const char* device = "/dev/urandom"; |
| ssize_t num_bytes; |
| bool readurandom = true; |
| #if defined(HAVE_GETRANDOM) |
| num_bytes = static_cast<ssize_t>(syscall(SYS_getrandom, &seed, sizeof(seed), GRND_NONBLOCK)); |
| if (num_bytes < static_cast<ssize_t>(sizeof(seed))) { |
| char buf[STRERROR_BUFSIZE_]; |
| char* err = STRERROR_R_(errno, buf, sizeof(buf)); |
| LOG_WARN("Unable to read %u random bytes (%s): %u read", |
| static_cast<unsigned int>(sizeof(seed)), err, static_cast<unsigned int>(num_bytes)); |
| } else { |
| readurandom = false; |
| } |
| #endif // defined(HAVE_GETRANDOM) |
| |
| if (readurandom) { |
| int fd = open(device, O_RDONLY); |
| |
| if (fd < 0) { |
| char buf[STRERROR_BUFSIZE_]; |
| char* err = STRERROR_R_(errno, buf, sizeof(buf)); |
| LOG_CRITICAL("Unable to open random device (%s): %s", device, err); |
| return seed; |
| } |
| |
| num_bytes = read(fd, reinterpret_cast<char*>(&seed), sizeof(seed)); |
| if (num_bytes < 0) { |
| char buf[STRERROR_BUFSIZE_]; |
| char* err = STRERROR_R_(errno, buf, sizeof(buf)); |
| LOG_CRITICAL("Unable to read from random device (%s): %s", device, err); |
| } else if (num_bytes != sizeof(seed)) { |
| char buf[STRERROR_BUFSIZE_]; |
| char* err = STRERROR_R_(errno, buf, sizeof(buf)); |
| LOG_CRITICAL("Unable to read full seed value (expected: %u read: %u) " |
| "from random device (%s): %s", |
| static_cast<unsigned int>(sizeof(seed)), static_cast<unsigned int>(num_bytes), |
| device, err); |
| } |
| |
| close(fd); |
| } |
| #endif // defined(HAVE_ARC4RANDOM) |
| |
| return seed; |
| } |
| #endif |
| |
| }} // namespace datastax::internal |