[Core] Add new switch_rand() a compliant random number generator API. Add a unit-test.

* [Core] Add new switch_rand() a compliant random number generator API. Add a unit-test.

* Fall back to rand() on unsupported platforms compile time.
This commit is contained in:
Andrey Volk 2024-06-06 21:05:33 +03:00
parent b84b7cbf71
commit c7e793c345
3 changed files with 83 additions and 0 deletions

View File

@ -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(char *) switch_must_strdup(const char *_s);
SWITCH_DECLARE(const char *) switch_memory_usage_stream(switch_stream_handle_t *stream); 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 SWITCH_END_EXTERN_C
#endif #endif
/* For Emacs: /* For Emacs:

View File

@ -4811,6 +4811,63 @@ done:
return status; 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: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c

View File

@ -53,6 +53,27 @@ FST_CORE_BEGIN("./conf")
} }
FST_TEARDOWN_END() 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) FST_TEST_BEGIN(test_switch_uint31_t_overflow)
{ {
switch_uint31_t x; switch_uint31_t x;