Skip to content

Commit

Permalink
Add support for UUID versioning and generation of UUIDv7.
Browse files Browse the repository at this point in the history
This update introduces support for generating UUIDs with specific versions, including the newly implemented UUIDv7. The `create_uuid` API has been updated to accept optional version parameters. Additionally, a new utility function `switch_getentropy` was added to ensure secure random number generation.
  • Loading branch information
ar45 committed Jan 15, 2025
1 parent ca5b327 commit 0ff4e66
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 7 deletions.
7 changes: 6 additions & 1 deletion src/include/switch_apr.h
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,12 @@ SWITCH_DECLARE(void) switch_uuid_format(char *buffer, const switch_uuid_t *uuid)
* Generate and return a (new) UUID
* @param uuid The resulting UUID
*/
SWITCH_DECLARE(void) switch_uuid_get(switch_uuid_t *uuid);
#ifndef DEFAULT_UUID_VERSION
#define DEFAULT_UUID_VERSION 4
#endif
SWITCH_DECLARE(switch_status_t) switch_uuid_generate_v4(switch_uuid_t *uuid);
SWITCH_DECLARE(switch_status_t) switch_uuid_generate_version(switch_uuid_t *uuid, int version);
#define switch_uuid_get(uuid) switch_uuid_generate_version(uuid, DEFAULT_UUID_VERSION);

/**
* Parse a standard-format string into a UUID
Expand Down
6 changes: 5 additions & 1 deletion src/include/switch_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -1421,7 +1421,8 @@ SWITCH_DECLARE(void *) switch_calloc(size_t nmemb, size_t size);
SWITCH_DECLARE(const char *) switch_inet_ntop(int af, void const *src, char *dst, size_t size);
#endif

SWITCH_DECLARE(char *) switch_uuid_str(char *buf, switch_size_t len);
#define switch_uuid_str(buf, len) switch_uuid_str_version(buf, len, DEFAULT_UUID_VERSION);
SWITCH_DECLARE(char *) switch_uuid_str_version(char *buf, switch_size_t len, int version);
SWITCH_DECLARE(char *) switch_format_number(const char *num);

SWITCH_DECLARE(unsigned int) switch_atoui(const char *nptr);
Expand Down Expand Up @@ -1518,6 +1519,9 @@ SWITCH_DECLARE(const char *) switch_memory_usage_stream(switch_stream_handle_t *
/ Compliant random number generator. Returns the value between 0 and 0x7fff (RAND_MAX).
**/
SWITCH_DECLARE(int) switch_rand(void);
SWITCH_DECLARE(int) switch_getentropy(void *buffer, switch_size_t length);
SWITCH_DECLARE(switch_status_t) switch_uuid_generate_v7(switch_uuid_t *uuid);
SWITCH_DECLARE(switch_status_t) switch_uuid_generate_v7(switch_uuid_t *uuid);

SWITCH_END_EXTERN_C
#endif
Expand Down
9 changes: 7 additions & 2 deletions src/mod/applications/mod_commands/mod_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -3162,11 +3162,16 @@ SWITCH_STANDARD_API(tone_detect_session_function)
return SWITCH_STATUS_SUCCESS;
}

#define UUID_GENERATE_SYNTAX "<create_uuid> [4|7]"
SWITCH_STANDARD_API(uuid_function)
{
char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
int version = DEFAULT_UUID_VERSION;
if (!zstr(cmd)) {
version = atoi(cmd);
}

switch_uuid_str(uuid_str, sizeof(uuid_str));
switch_uuid_str_version(uuid_str, sizeof(uuid_str), version);

stream->write_function(stream, "%s", uuid_str);
return SWITCH_STATUS_SUCCESS;
Expand Down Expand Up @@ -7598,7 +7603,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
SWITCH_ADD_API(commands_api_interface, "cond", "Evaluate a conditional", cond_function, "<expr> ? <true val> : <false val>");
SWITCH_ADD_API(commands_api_interface, "console_complete", "", console_complete_function, "<line>");
SWITCH_ADD_API(commands_api_interface, "console_complete_xml", "", console_complete_xml_function, "<line>");
SWITCH_ADD_API(commands_api_interface, "create_uuid", "Create a uuid", uuid_function, UUID_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "create_uuid", "Create a uuid", uuid_function, UUID_GENERATE_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "db_cache", "Manage db cache", db_cache_function, "status");
SWITCH_ADD_API(commands_api_interface, "domain_data", "Find domain data", domain_data_function, "<domain> [var|param|attr] <name>");
SWITCH_ADD_API(commands_api_interface, "domain_exists", "Check if a domain exists", domain_exists_function, "<domain>");
Expand Down
17 changes: 16 additions & 1 deletion src/switch_apr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,7 @@ SWITCH_DECLARE(void) switch_uuid_format(char *buffer, const switch_uuid_t *uuid)
#endif
}

SWITCH_DECLARE(void) switch_uuid_get(switch_uuid_t *uuid)
SWITCH_DECLARE(void) switch_uuid_generate_v4(switch_uuid_t *uuid)
{
switch_mutex_lock(runtime.uuid_mutex);
#ifndef WIN32
Expand All @@ -1160,6 +1160,21 @@ SWITCH_DECLARE(void) switch_uuid_get(switch_uuid_t *uuid)
switch_mutex_unlock(runtime.uuid_mutex);
}

SWITCH_DECLARE(switch_status_t) switch_uuid_generate_version(switch_uuid_t *uuid, int version)
{
switch(version) {
case 4:
switch_uuid_generate_v4(uuid);
break;
case 7:
switch_uuid_generate_v7(uuid);
break;
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: Unsupported UUID version %d\n", version);
return SWITCH_STATUS_FALSE;
}
}

SWITCH_DECLARE(switch_status_t) switch_uuid_parse(switch_uuid_t *uuid, const char *uuid_str)
{
#ifndef WIN32
Expand Down
98 changes: 96 additions & 2 deletions src/switch_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -4110,14 +4110,14 @@ SWITCH_DECLARE(int) switch_split_user_domain(char *in, char **user, char **domai
}


SWITCH_DECLARE(char *) switch_uuid_str(char *buf, switch_size_t len)
SWITCH_DECLARE(char *) switch_uuid_str_version(char *buf, switch_size_t len, int version)
{
switch_uuid_t uuid;

if (len < (SWITCH_UUID_FORMATTED_LENGTH + 1)) {
switch_snprintf(buf, len, "INVALID");
} else {
switch_uuid_get(&uuid);
switch_uuid_generate_version(&uuid, version);
switch_uuid_format(buf, &uuid);
}

Expand Down Expand Up @@ -4872,6 +4872,100 @@ SWITCH_DECLARE(int) switch_rand(void)
#endif
}


SWITCH_DECLARE(int) switch_getentropy(void *buffer, switch_size_t length)
{
if (!buffer || length > 256) { // Enforce same limit as `getentropy`
errno = EIO; // Input/Output error
return -1;
}

#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);
errno = EIO; // Input/Output error
return -1;
}

status = BCryptGenRandom(hAlgorithm, (PUCHAR)buffer, (ULONG)length, 0);
if (!BCRYPT_SUCCESS(status)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "BCryptGenRandom failed with status %d\n", status);
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
errno = EIO; // Input/Output error
return -1;
}

BCryptCloseAlgorithmProvider(hAlgorithm, 0);
return 0;

#elif defined(__unix__) || defined(__APPLE__)
int random_fd = open("/dev/urandom", O_RDONLY);
switch_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);
errno = EIO; // Input/Output error
return -1;
}

result = read(random_fd, buffer, length);
if (result < 0 || (switch_size_t)result != length) {
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);
close(random_fd);
errno = EIO; // Input/Output error
return -1;
}

close(random_fd);
return 0;

#else
// Fallback: Use rand() for platforms that do not support secure randomness
unsigned char *buf = (unsigned char *)buffer;
for (switch_size_t i = 0; i < length; i++) {
buf[i] = (unsigned char)(rand() & 0xFF); // Generate byte-wise randomness
}
return 0;
#endif
}


SWITCH_DECLARE(switch_status_t) switch_uuid_generate_v7(switch_uuid_t *uuid) {
/* random bytes */
unsigned char *value = uuid->data;

if (switch_getentropy(value, 16) != 0) {
return -1;
}

/* current timestamp in ms */
switch_time_t timestamp = switch_time_now() / 1000;

// timestamp
value[0] = (timestamp >> 40) & 0xFF;
value[1] = (timestamp >> 32) & 0xFF;
value[2] = (timestamp >> 24) & 0xFF;
value[3] = (timestamp >> 16) & 0xFF;
value[4] = (timestamp >> 8) & 0xFF;
value[5] = timestamp & 0xFF;

// version and variant
value[6] = (value[6] & 0x0F) | 0x70;
value[8] = (value[8] & 0x3F) | 0x80;

return SWITCH_STATUS_SUCCESS;
}

/* For Emacs:
* Local Variables:
* mode:c
Expand Down

0 comments on commit 0ff4e66

Please sign in to comment.