/* ** 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_md5.h" #include "uuid_prng.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 */ typedef struct { 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_obj_t; /* abstract data type (ADT) of API */ struct uuid_st { uuid_obj_t obj; /* inlined UUID object */ prng_t *prng; /* RPNG sub-object */ md5_t *md5; /* MD5 sub-object */ }; /* create UUID object */ uuid_rc_t uuid_create(uuid_t **uuid) { /* argument sanity check */ if (uuid == NULL) return UUID_RC_ARG; /* allocate UUID object */ if ((*uuid = (uuid_t *)malloc(sizeof(uuid_t))) == NULL) return UUID_RC_MEM; /* set UUID object initially to "nil UUID" */ uuid_nil(*uuid); /* create PRNG and MD5 sub-objects */ if (prng_create(&(*uuid)->prng) != PRNG_RC_OK) return UUID_RC_INT; if (md5_create(&(*uuid)->md5) != MD5_RC_OK) return UUID_RC_INT; return UUID_RC_OK; } /* destroy UUID object */ uuid_rc_t uuid_destroy(uuid_t *uuid) { /* argument sanity check */ if (uuid == NULL) return UUID_RC_ARG; /* destroy PRNG and MD5 sub-objects */ prng_destroy(uuid->prng); md5_destroy(uuid->md5); /* free UUID object */ free(uuid); return UUID_RC_OK; } /* set UUID object to represents 'nil UUID' */ 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((void *)&(uuid->obj), '\0', sizeof(uuid->obj)); return UUID_RC_OK; } /* compare UUID objects */ uuid_rc_t uuid_compare(uuid_t *uuid1, uuid_t *uuid2, 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 (uuid1 == uuid2) RESULT(0); if (uuid1 == NULL && uuid2 == NULL) RESULT(0); if (uuid1 == NULL) RESULT((uuid_isnil(uuid2, &r), r) ? 0 : -1); if (uuid2 == NULL) RESULT((uuid_isnil(uuid1, &r), r) ? 0 : 1); /* standard cases: regular different UUIDs */ if (uuid1->obj.time_low != uuid2->obj.time_low) RESULT((uuid1->obj.time_low < uuid2->obj.time_low) ? -1 : 1); if ((r = (int)uuid1->obj.time_mid - (int)uuid2->obj.time_mid) != 0) RESULT((r < 0) ? -1 : 1); if ((r = (int)uuid1->obj.time_hi_and_version - (int)uuid2->obj.time_hi_and_version) != 0) RESULT((r < 0) ? -1 : 1); if ((r = (int)uuid1->obj.clock_seq_hi_and_reserved - (int)uuid2->obj.clock_seq_hi_and_reserved) != 0) RESULT((r < 0) ? -1 : 1); if ((r = (int)uuid1->obj.clock_seq_low - (int)uuid2->obj.clock_seq_low) != 0) RESULT((r < 0) ? -1 : 1); if ((r = memcmp(uuid1->obj.node, uuid2->obj.node, sizeof(uuid1->obj.node))) != 0) RESULT((r < 0) ? -1 : 1); /* default case: the keys are equal */ *result = 0; result_exit: return UUID_RC_OK; } /* check whether UUID object represents 'nil UUID' */ 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->obj); i < UUID_LEN_BIN; i++) { if (*ucp++ != '\0') { *result = FALSE; break; } } return UUID_RC_OK; } /* unpack UUID binary presentation into UUID object (allows in-place operation for internal efficiency!) */ 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->obj.time_low = tmp32; /* unpack "time_mid" field */ tmp16 = *in++; tmp16 = (tmp16 << 8) | *in++; uuid->obj.time_mid = tmp16; /* unpack "time_hi_and_version" field */ tmp16 = *in++; tmp16 = (tmp16 << 8) | *in++; uuid->obj.time_hi_and_version = tmp16; /* unpack "clock_seq_hi_and_reserved" field */ uuid->obj.clock_seq_hi_and_reserved = *in++; /* unpack "clock_seq_low" field */ uuid->obj.clock_seq_low = *in++; /* unpack "node" field */ for (i = 0; i < sizeof(uuid->obj.node); i++) uuid->obj.node[i] = *in++; return UUID_RC_OK; } /* pack UUID object into binary representation (allows in-place operation for internal efficiency!) */ 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->obj.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->obj.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->obj.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->obj.clock_seq_hi_and_reserved; /* pack "clock_seq_low" field */ out[9] = uuid->obj.clock_seq_low; /* pack "node" field */ for (i = 0; i < sizeof(uuid->obj.node); i++) out[10+i] = uuid->obj.node[i]; return UUID_RC_OK; } /* INTERNAL: check for valid UUID string representation syntax */ static int uuid_isstr(const char *str) { int i; const char *cp; /* example reference: f81d4fae-7dec-11d0-a765-00a0c91e6bf6 012345678901234567890123456789012345 0 1 2 3 */ if (str == NULL) return FALSE; if (strlen(str) != UUID_LEN_STR) return FALSE; 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 FALSE; } if (i == UUID_LEN_STR) if (*cp == '\0') continue; if (!isxdigit(*cp)) return FALSE; } return TRUE; } /* parse string representation into UUID object */ 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; /* check for correct UUID string representation syntax */ if (!uuid_isstr(str)) return UUID_RC_ARG; /* parse hex values of "time" parts */ uuid->obj.time_low = (uuid_uint32_t)strtoul(str, NULL, 16); uuid->obj.time_mid = (uuid_uint16_t)strtoul(str+9, NULL, 16); uuid->obj.time_hi_and_version = (uuid_uint16_t)strtoul(str+14, NULL, 16); /* parse hex values of "clock" parts */ tmp16 = (uuid_uint16_t)strtoul(str+19, NULL, 16); uuid->obj.clock_seq_low = (uuid_uint8_t)(tmp16 & 0xff); tmp16 >>= 8; uuid->obj.clock_seq_hi_and_reserved = (uuid_uint8_t)(tmp16 & 0xff); /* parse hex values of "node" part */ cp = str+24; hexbuf[2] = '\0'; for (i = 0; i < sizeof(uuid->obj.node); i++) { hexbuf[0] = *cp++; hexbuf[1] = *cp++; uuid->obj.node[i] = strtoul(hexbuf, NULL, 16); } return UUID_RC_OK; } /* format UUID object into string representation */ 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->obj.time_low, (unsigned int)uuid->obj.time_mid, (unsigned int)uuid->obj.time_hi_and_version, (unsigned int)uuid->obj.clock_seq_hi_and_reserved, (unsigned int)uuid->obj.clock_seq_low, (unsigned int)uuid->obj.node[0], (unsigned int)uuid->obj.node[1], (unsigned int)uuid->obj.node[2], (unsigned int)uuid->obj.node[3], (unsigned int)uuid->obj.node[4], (unsigned int)uuid->obj.node[5]); return UUID_RC_OK; } /* INTERNAL: brand UUID with version and variant */ static void uuid_brand(uuid_t *uuid, int version) { /* set version (as given) */ uuid->obj.time_hi_and_version &= 0x0fff; uuid->obj.time_hi_and_version |= (((uuid_uint16_t)version & 0x0fff) << 12); /* set variant (always DCE 1.1 only) */ uuid->obj.clock_seq_hi_and_reserved &= ~((0x03) << 6); uuid->obj.clock_seq_hi_and_reserved |= (0x02 << 6); return; } /* INTERNAL: generate UUID version 1, time part */ static uuid_rc_t uuid_generate_v1_time(uuid_t *uuid, unsigned int mode, va_list ap) { struct timeval tv; ui64_t t; ui64_t offset; ui64_t ov; /* determine current system time */ if (gettimeofday(&tv, NULL) == -1) return UUID_RC_SYS; /* convert from timeval (sec,usec) to OSSP ui64 (100*nsec) format */ t = ui64_n2i(tv.tv_sec); t = ui64_muln(t, 1000000, NULL); t = ui64_addn(t, tv.tv_usec, NULL); t = ui64_muln(t, 10, NULL); /* adjust for offset between UUID and Unix Epoch time through adding the magic offset 01B21DD213814000 from draft-leach-uuids-guids-01. (UUID UTC base time is October 15, 1582 Unix UTC base time is January 1, 1970) */ offset = ui64_s2i("01B21DD213814000", NULL, 16); t = ui64_add(t, offset, NULL); /* store the 60 LSB of the time in the UUID */ t = ui64_rol(t, 16, &ov); uuid->obj.time_hi_and_version = (uuid_uint16_t)(ui64_i2n(ov) & 0x00000fff); /* 12 of 16 bit only! */ t = ui64_rol(t, 16, &ov); uuid->obj.time_mid = (uuid_uint16_t)(ui64_i2n(ov) & 0x0000ffff); /* all 16 bit */ t = ui64_rol(t, 32, &ov); uuid->obj.time_low = (uuid_uint32_t)(ui64_i2n(ov) & 0xffffffff); /* all 32 bit */ return UUID_RC_OK; } /* INTERNAL: generate UUID version 1, clock part */ static uuid_rc_t uuid_generate_v1_clock(uuid_t *uuid, unsigned int mode, va_list ap) { /* FIXME */ return UUID_RC_OK; } /* INTERNAL: generate UUID version 1, node part */ static uuid_rc_t uuid_generate_v1_node(uuid_t *uuid, unsigned int mode, va_list ap) { /* FIXME */ return UUID_RC_OK; } /* INTERNAL: 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) { uuid_rc_t rc; /* generate individual parts of v1 UUID */ if ((rc = uuid_generate_v1_time (uuid, mode, ap)) != UUID_RC_OK) return rc; if ((rc = uuid_generate_v1_clock(uuid, mode, ap)) != UUID_RC_OK) return rc; if ((rc = uuid_generate_v1_node (uuid, mode, ap)) != UUID_RC_OK) return rc; /* brand with version and variant */ uuid_brand(uuid, 1); return UUID_RC_OK; } /* INTERNAL: UUID Namespace Ids as pre-defined by draft-leach-uuids-guids-01.txt (defined here as network byte ordered octet stream for direct MD5 feeding) */ static struct { char *name; uuid_uint8_t uuid[UUID_LEN_BIN]; } uuid_ns_table[] = { { "DNS", /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */ { 0x6b,0xa7,0xb8,0x10,0x9d,0xad,0x11,0xd1,0x80,0xb4,0x00,0xc0,0x4f,0xd4,0x30,0xc8 } }, { "URL", /* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 */ { 0x6b,0xa7,0xb8,0x11,0x9d,0xad,0x11,0xd1,0x80,0xb4,0x00,0xc0,0x4f,0xd4,0x30,0xc8 } }, { "OID", /* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 */ { 0x6b,0xa7,0xb8,0x12,0x9d,0xad,0x11,0xd1,0x80,0xb4,0x00,0xc0,0x4f,0xd4,0x30,0xc8 } }, { "X500", /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 */ { 0x6b,0xa7,0xb8,0x14,0x9d,0xad,0x11,0xd1,0x80,0xb4,0x00,0xc0,0x4f,0xd4,0x30,0xc8 } } }; /* INTERNAL: generate UUID version 3: name based */ static uuid_rc_t uuid_generate_v3(uuid_t *uuid, unsigned int mode, va_list ap) { char *str; char *ns; void *uuid_octets; uuid_t *uuid_object; uuid_rc_t rc; int i; /* determine namespace UUID name and argument name string */ if ((ns = (char *)va_arg(ap, char *)) == NULL) return UUID_RC_ARG; if ((str = (char *)va_arg(ap, char *)) == NULL) return UUID_RC_ARG; /* initialize MD5 context */ if (md5_init(uuid->md5) != MD5_RC_OK) return UUID_RC_MEM; /* load the namespace UUID into MD5 context */ if (uuid_isstr(ns)) { /* custom namespace via UUID string representation */ if ((rc = uuid_create(&uuid_object)) != UUID_RC_OK) return rc; if ((rc = uuid_parse(uuid_object, ns)) != UUID_RC_OK) return rc; uuid_octets = (void *)&(uuid_object->obj); uuid_pack(uuid_object, &uuid_octets); md5_update(uuid->md5, uuid_octets, UUID_LEN_BIN); uuid_destroy(uuid_object); } else { /* standard namespace via UUID namespace id */ uuid_octets = NULL; for (i = 0; i < sizeof(uuid_ns_table)/sizeof(uuid_ns_table[0]); i++) { if (strcmp(uuid_ns_table[i].name, ns) == 0) { uuid_octets = uuid_ns_table[i].uuid; break; } } if (uuid_octets == NULL) return UUID_RC_ARG; md5_update(uuid->md5, uuid_octets, UUID_LEN_BIN); } /* load the argument name string into MD5 context */ md5_update(uuid->md5, str, strlen(str)); /* store MD5 result into UUID (requires MD5_LEN_BIN space, UUID_LEN_BIN space is available, and both are equal in size, so we are safe!) */ uuid_octets = (void *)&(uuid->obj); md5_store(uuid->md5, &uuid_octets, NULL); /* fulfill requirement of standard and convert UUID data into local/host byte order (this uses fact that uuid_unpack() is able to operate in-place!) */ uuid_unpack(uuid, (void *)&(uuid->obj)); /* brand UUID with version and variant */ uuid_brand(uuid, 3); return UUID_RC_OK; } /* INTERNAL: generate UUID version 4: random number based */ static uuid_rc_t uuid_generate_v4(uuid_t *uuid, unsigned int mode, va_list ap) { /* fill UUID with random data */ prng_data(uuid->prng, (void *)&(uuid->obj), sizeof(uuid->obj)); /* brand UUID with version and variant */ uuid_brand(uuid, 4); return UUID_RC_OK; } /* generate UUID */ 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; } /* dump UUID object as descriptive text */ 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; } /* translate UUID API error code into corresponding error string */ 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; }