/* ** OSSP uuid - Universally Unique Identifier ** Copyright (c) 2004 Ralf S. Engelschall ** Copyright (c) 2004 The OSSP Project ** ** This file is part of OSSP uuid, a library for the generation ** of UUIDs which can found at http://www.ossp.org/pkg/lib/uuid/ ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** uuid.c: library API implementation */ #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "uuid.h" #include "uuid_ui64.h" /* determine types of 8-bit size */ #if SIZEOF_CHAR == 1 typedef char uuid_int8_t; #else #error uexpected: sizeof(char) != 1 !? #endif #if SIZEOF_UNSIGNED_CHAR == 1 typedef unsigned char uuid_uint8_t; #else #error uexpected: sizeof(unsigned char) != 1 !? #endif /* determine types of 16-bit size */ #if SIZEOF_SHORT == 2 typedef short uuid_int16_t; #elif SIZEOF_INT == 2 typedef int uuid_int16_t; #elif SIZEOF_LONG == 2 typedef long uuid_int16_t; #else #error unexpected: no type found for uuid_int16_t #endif #if SIZEOF_UNSIGNED_SHORT == 2 typedef unsigned short uuid_uint16_t; #elif SIZEOF_UNSIGNED_INT == 2 typedef unsigned int uuid_uint16_t; #elif SIZEOF_UNSIGNED_LONG == 2 typedef unsigned long uuid_uint16_t; #else #error unexpected: no type found for uuid_uint16_t #endif /* determine types of 32-bit size */ #if SIZEOF_SHORT == 4 typedef short uuid_int32_t; #elif SIZEOF_INT == 4 typedef int uuid_int32_t; #elif SIZEOF_LONG == 4 typedef long uuid_int32_t; #elif SIZEOF_LONG_LONG == 4 typedef long long uuid_int32_t; #else #error unexpected: no type found for uuid_int32_t #endif #if SIZEOF_UNSIGNED_SHORT == 4 typedef unsigned short uuid_uint32_t; #elif SIZEOF_UNSIGNED_INT == 4 typedef unsigned int uuid_uint32_t; #elif SIZEOF_UNSIGNED_LONG == 4 typedef unsigned long uuid_uint32_t; #elif SIZEOF_UNSIGNED_LONG_LONG == 4 typedef unsigned long long uuid_uint32_t; #else #error unexpected: no type found for uuid_uint32_t #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE !FALSE #endif /* UUID binary representation according to UUID standards */ struct uuid_st { uuid_uint32_t time_low; uuid_uint16_t time_mid; uuid_uint16_t time_hi_and_version; uuid_uint8_t clock_seq_hi_and_reserved; uuid_uint8_t clock_seq_low; uuid_uint8_t node[6]; }; uuid_rc_t uuid_create(uuid_t **uuid) { /* argument sanity check */ if (uuid == NULL) return UUID_RC_ARG; /* allocate UUID binary representation buffer */ if ((*uuid = (uuid_t *)malloc(sizeof(uuid_t))) == NULL) return UUID_RC_MEM; /* set initially to "nil UUID" */ uuid_nil(*uuid); return UUID_RC_OK; } uuid_rc_t uuid_destroy(uuid_t *uuid) { /* argument sanity check */ if (uuid == NULL) return UUID_RC_ARG; /* free UUID binary representation buffer */ free(uuid); return UUID_RC_OK; } uuid_rc_t uuid_nil(uuid_t *uuid) { /* argument sanity check */ if (uuid == NULL) return UUID_RC_ARG; /* clear all octets to create "nil UUID" */ memset(uuid, '\0', sizeof(uuid_t)); return UUID_RC_OK; } uuid_rc_t uuid_compare(uuid_t *a, uuid_t *b, int *result) { int r; /* argument sanity check */ if (result == NULL) return UUID_RC_ARG; /* convinience macro for setting result */ # define RESULT(r) \ do { \ *result = (r); \ goto result_exit; \ } while (0) /* special cases: NULL or equal UUIDs */ if (a == b) RESULT(0); if (a == NULL && b == NULL) RESULT(0); if (a == NULL) RESULT((uuid_isnil(b, &r), r) ? 0 : -1); if (b == NULL) RESULT((uuid_isnil(a, &r), r) ? 0 : 1); /* standard cases: regular different UUIDs */ if (a->time_low != b->time_low) RESULT((a->time_low < b->time_low) ? -1 : 1); if ((r = (int)a->time_mid - (int)b->time_mid) != 0) RESULT((r < 0) ? -1 : 1); if ((r = (int)a->time_hi_and_version - (int)b->time_hi_and_version) != 0) RESULT((r < 0) ? -1 : 1); if ((r = (int)a->clock_seq_hi_and_reserved - (int)b->clock_seq_hi_and_reserved) != 0) RESULT((r < 0) ? -1 : 1); if ((r = (int)a->clock_seq_low - (int)b->clock_seq_low) != 0) RESULT((r < 0) ? -1 : 1); if ((r = memcmp(a->node, b->node, sizeof(a->node))) != 0) RESULT((r < 0) ? -1 : 1); /* default case: the keys are equal */ *result = 0; result_exit: return UUID_RC_OK; } uuid_rc_t uuid_isnil(uuid_t *uuid, int *result) { const unsigned char *ucp; int i; /* sanity check argument(s) */ if (uuid == NULL || result == NULL) return UUID_RC_ARG; /* a "nil UUID" is defined as all octets zero, so check for this case */ *result = TRUE; for (i = 0, ucp = (unsigned char *)uuid; i < UUID_LEN_BIN; i++) { if (*ucp++ != '\0') { *result = FALSE; break; } } return UUID_RC_OK; } uuid_rc_t uuid_unpack(uuid_t *uuid, const void *buf) { const uuid_uint8_t *in; uuid_uint32_t tmp32; uuid_uint16_t tmp16; int i; /* sanity check argument(s) */ if (uuid == NULL || buf == NULL) return UUID_RC_ARG; /* treat input buffer as octet stream */ in = (const uuid_uint8_t *)buf; /* unpack "time_low" field */ tmp32 = *in++; tmp32 = (tmp32 << 8) | *in++; tmp32 = (tmp32 << 8) | *in++; tmp32 = (tmp32 << 8) | *in++; uuid->time_low = tmp32; /* unpack "time_mid" field */ tmp16 = *in++; tmp16 = (tmp16 << 8) | *in++; uuid->time_mid = tmp16; /* unpack "time_hi_and_version" field */ tmp16 = *in++; tmp16 = (tmp16 << 8) | *in++; uuid->time_hi_and_version = tmp16; /* unpack "clock_seq_hi_and_reserved" field */ uuid->clock_seq_hi_and_reserved = *in++; /* unpack "clock_seq_low" field */ uuid->clock_seq_low = *in++; /* unpack "node" field */ for (i = 0; i < sizeof(uuid->node); i++) uuid->node[i] = *in++; return UUID_RC_OK; } uuid_rc_t uuid_pack(uuid_t *uuid, void **buf) { uuid_uint8_t *out; uuid_uint32_t tmp32; uuid_uint16_t tmp16; int i; /* sanity check argument(s) */ if (uuid == NULL || buf == NULL) return UUID_RC_ARG; /* optionally allocate octet buffer */ if (*buf == NULL) if ((*buf = malloc(sizeof(uuid_t))) == NULL) return UUID_RC_MEM; /* treat output buffer as octet stream */ out = (uuid_uint8_t *)(*buf); /* pack "time_low" field */ tmp32 = uuid->time_low; out[3] = (uuid_uint8_t)(tmp32 & 0xff); tmp32 >>= 8; out[2] = (uuid_uint8_t)(tmp32 & 0xff); tmp32 >>= 8; out[1] = (uuid_uint8_t)(tmp32 & 0xff); tmp32 >>= 8; out[0] = (uuid_uint8_t)(tmp32 & 0xff); /* pack "time_mid" field */ tmp16 = uuid->time_mid; out[5] = (uuid_uint8_t)(tmp16 & 0xff); tmp16 >>= 8; out[4] = (uuid_uint8_t)(tmp16 & 0xff); /* pack "time_hi_and_version" field */ tmp16 = uuid->time_hi_and_version; out[7] = (uuid_uint8_t)(tmp16 & 0xff); tmp16 >>= 8; out[6] = (uuid_uint8_t)(tmp16 & 0xff); /* pack "clock_seq_hi_and_reserved" field */ out[8] = uuid->clock_seq_hi_and_reserved; /* pack "clock_seq_low" field */ out[9] = uuid->clock_seq_low; /* pack "node" field */ for (i = 0; i < sizeof(uuid->node); i++) out[10+i] = uuid->node[i]; return UUID_RC_OK; } uuid_rc_t uuid_parse(uuid_t *uuid, const char *str) { uuid_uint16_t tmp16; const char *cp; char hexbuf[3]; int i; /* sanity check argument(s) */ if (uuid == NULL || str == NULL) return UUID_RC_ARG; /* * pass 1: check UUID string representation syntax * example reference: * f81d4fae-7dec-11d0-a765-00a0c91e6bf6 * 012345678901234567890123456789012345 * 0 1 2 3 */ if (strlen(str) != UUID_LEN_STR) return UUID_RC_ARG; for (i = 0, cp = str; i <= UUID_LEN_STR; i++, cp++) { if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) { if (*cp == '-') continue; else return -1; } if (i == UUID_LEN_STR) if (*cp == '\0') continue; if (!isxdigit(*cp)) return UUID_RC_ARG; } /* * pass 2: parse hex values of string representation syntax */ uuid->time_low = (uuid_uint32_t)strtoul(str, NULL, 16); uuid->time_mid = (uuid_uint16_t)strtoul(str+9, NULL, 16); uuid->time_hi_and_version = (uuid_uint16_t)strtoul(str+14, NULL, 16); tmp16 = (uuid_uint16_t)strtoul(str+19, NULL, 16); uuid->clock_seq_low = (uuid_uint8_t)(tmp16 & 0xff); tmp16 >>= 8; uuid->clock_seq_hi_and_reserved = (uuid_uint8_t)(tmp16 & 0xff); cp = str+24; hexbuf[2] = '\0'; for (i = 0; i < sizeof(uuid->node); i++) { hexbuf[0] = *cp++; hexbuf[1] = *cp++; uuid->node[i] = strtoul(hexbuf, NULL, 16); } return UUID_RC_OK; } uuid_rc_t uuid_format(uuid_t *uuid, char **str) { /* sanity check argument(s) */ if (uuid == NULL || str == NULL) return UUID_RC_ARG; /* optionally allocate string buffer */ if (*str == NULL) if ((*str = (char *)malloc(UUID_LEN_STR+1)) == NULL) return UUID_RC_MEM; /* format UUID into string representation */ sprintf(*str, "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", (unsigned long)uuid->time_low, (unsigned int)uuid->time_mid, (unsigned int)uuid->time_hi_and_version, (unsigned int)uuid->clock_seq_hi_and_reserved, (unsigned int)uuid->clock_seq_low, (unsigned int)uuid->node[0], (unsigned int)uuid->node[1], (unsigned int)uuid->node[2], (unsigned int)uuid->node[3], (unsigned int)uuid->node[4], (unsigned int)uuid->node[5]); return UUID_RC_OK; } /* pseudo-random number generator (PRNG) */ static void uuid_prng_getdata(uuid_uint8_t *data_ptr, size_t data_len) { static int initialized = FALSE; static int fd = -1; struct timeval tv; pid_t pid; size_t n; size_t i; uuid_uint8_t *p; int cnt; if (!initialized) { /* try to open the system PRNG device */ if ((fd = open("/dev/urandom", O_RDONLY)) == -1) fd = open("/dev/random", O_RDONLY|O_NONBLOCK); if (fd != -1) fcntl(fd, F_SETFD, FD_CLOEXEC); /* seed the PRNG once */ gettimeofday(&tv, NULL); pid = getpid(); srand((unsigned int)( ((uuid_uint32_t)pid << 16) ^ (uuid_uint32_t)pid ^ (uuid_uint32_t)tv.tv_sec ^ (uuid_uint32_t)tv.tv_usec)); /* crank the PRNG a few times */ gettimeofday(&tv, NULL); for (i = (unsigned int)(tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) (void)rand(); initialized = TRUE; } /* try to gather data from the system PRNG device */ if (fd != -1) { p = data_ptr; n = data_len; cnt = 0; while (n > 0) { i = read(fd, (void *)p, n); if (i <= 0) { if (cnt++ > 16) break; continue; } n -= i; p += i; cnt = 0; } } /* always also apply the weaker PRNG. In case the stronger PRNG device based source failed, this is the only remaining randomness, of course */ for (p = data_ptr, n = 0; n < data_len; n++) *p++ ^= (uuid_uint8_t)(((uuid_uint32_t)rand() >> 7) & 0xFF); return; } /* brand UUID with version and variant */ static void uuid_brand(uuid_t *uuid, int version) { /* set version (as given) */ uuid->time_hi_and_version &= 0x0fff; uuid->time_hi_and_version |= (((uuid_uint16_t)version & 0x0fff) << 12); /* set variant (always DCE 1.1 only) */ uuid->clock_seq_hi_and_reserved &= ~((0x03) << 6); uuid->clock_seq_hi_and_reserved |= (0x02 << 6); return; } /* generate UUID version 1: time, clock and node based */ static uuid_rc_t uuid_generate_v1(uuid_t *uuid, unsigned int mode, va_list ap) { /* brand with version and variant */ uuid_brand(uuid, 1); /* FIXME */ return UUID_RC_OK; } /* generate UUID version 3: name based */ static uuid_rc_t uuid_generate_v3(uuid_t *uuid, unsigned int mode, va_list ap) { /* brand with version and variant */ uuid_brand(uuid, 3); /* FIXME */ return UUID_RC_OK; } /* generate UUID version 4: random number based */ static uuid_rc_t uuid_generate_v4(uuid_t *uuid, unsigned int mode, va_list ap) { /* fill with random data */ uuid_prng_getdata((void *)uuid, sizeof(uuid_t)); /* brand with version and variant */ uuid_brand(uuid, 4); return UUID_RC_OK; } uuid_rc_t uuid_generate(uuid_t *uuid, unsigned int mode, ...) { va_list ap; uuid_rc_t rc; /* sanity check argument(s) */ if (uuid == NULL) return UUID_RC_ARG; /* dispatch into version dependent generation functions */ va_start(ap, mode); if (mode & UUID_VERSION1) rc = uuid_generate_v1(uuid, mode, ap); else if (mode & UUID_VERSION3) rc = uuid_generate_v3(uuid, mode, ap); else if (mode & UUID_VERSION4) rc = uuid_generate_v4(uuid, mode, ap); else rc = UUID_RC_ARG; va_end(ap); return rc; } uuid_rc_t uuid_dump(uuid_t *uuid, char **str) { /* sanity check argument(s) */ if (uuid == NULL || str == NULL) return UUID_RC_ARG; /* FIXME */ return UUID_RC_OK; } char *uuid_error(uuid_rc_t rc) { char *str; switch (rc) { case UUID_RC_OK: str = "everything ok"; break; case UUID_RC_ARG: str = "invalid argument"; break; case UUID_RC_MEM: str = "out of memory"; break; case UUID_RC_SYS: str = "system error"; break; default: str = NULL; break; } return str; }