diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index 1d33939f4c..a0c91e6384 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -1514,6 +1514,11 @@ SWITCH_DECLARE(switch_status_t) switch_digest_string(const char *digest_name, ch SWITCH_DECLARE(char *) switch_must_strdup(const char *_s); SWITCH_DECLARE(const char *) switch_memory_usage_stream(switch_stream_handle_t *stream); +/** +/ Compliant random number generator. Returns the value between 0 and 0x7fff (RAND_MAX). +**/ +SWITCH_DECLARE(int) switch_rand(void); + SWITCH_END_EXTERN_C #endif /* For Emacs: diff --git a/src/switch_utils.c b/src/switch_utils.c index faffc1cc04..25d0bf76ee 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -4811,6 +4811,63 @@ done: return status; } +SWITCH_DECLARE(int) switch_rand(void) +{ + uint32_t random_number = 0; +#ifdef WIN32 + BCRYPT_ALG_HANDLE hAlgorithm = NULL; + NTSTATUS status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_RNG_ALGORITHM, NULL, 0); + + if (!BCRYPT_SUCCESS(status)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "BCryptOpenAlgorithmProvider failed with status %d\n", status); + + return 1; + } + + status = BCryptGenRandom(hAlgorithm, (PUCHAR)&random_number, sizeof(random_number), 0); + if (!BCRYPT_SUCCESS(status)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "BCryptGenRandom failed with status %d\n", status); + + return 1; + } + + BCryptCloseAlgorithmProvider(hAlgorithm, 0); + + /* Make sure we return from 0 to RAND_MAX */ + return (random_number & 0x7FFF); +#elif defined(__unix__) || defined(__APPLE__) + int random_fd = open("/dev/urandom", O_RDONLY); + ssize_t result; + char error_msg[100]; + + if (random_fd == -1) { + strncpy(error_msg, strerror(errno), sizeof(error_msg) - 1); + error_msg[sizeof(error_msg) - 1] = '\0'; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open failed: %s\n", error_msg); + + return 1; + } + + result = read(random_fd, &random_number, sizeof(random_number)); + if (result < 0) { + strncpy(error_msg, strerror(errno), sizeof(error_msg) - 1); + error_msg[sizeof(error_msg) - 1] = '\0'; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "read failed: %s\n", error_msg); + + return 1; + } + + close(random_fd); + + /* Make sure we return from 0 to RAND_MAX */ + return (random_number & 0x7FFF); +#else + return rand(); +#endif +} + /* For Emacs: * Local Variables: * mode:c diff --git a/tests/unit/switch_core.c b/tests/unit/switch_core.c index 2f06966582..295e4e0ff1 100644 --- a/tests/unit/switch_core.c +++ b/tests/unit/switch_core.c @@ -53,6 +53,27 @@ FST_CORE_BEGIN("./conf") } FST_TEARDOWN_END() + FST_TEST_BEGIN(test_switch_rand) + { + int i, c = 0; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "\nLet's generate a few random numbers.\n"); + + for (i = 0; i < 10; i++) { + uint32_t rnd = switch_rand(); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Random number %d\n", rnd); + + if (rnd == 1) { + c++; + } + } + + /* We do not expect all random numbers to be 1 all 10 times. That would mean we have an error OR we are lucky to have 10 random ones! */ + fst_check(c < 10); + } + FST_TEST_END() + FST_TEST_BEGIN(test_switch_uint31_t_overflow) { switch_uint31_t x;