*** /dev/null Fri Nov 22 14:56:37 2024
--- - Fri Nov 22 14:56:43 2024
***************
*** 0 ****
--- 1,1015 ----
+ /*
+ ** OSSP uuid - Universally Unique Identifier
+ ** Copyright (c) 2004 Ralf S. Engelschall <rse@engelschall.com>
+ ** Copyright (c) 2004 The OSSP Project <http://www.ossp.org/>
+ **
+ ** 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
+ */
+
+ /* system headers */
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <stdarg.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <ctype.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <time.h>
+ #include <sys/time.h>
+ #include <sys/types.h>
+
+ /* own headers */
+ #include "config.h"
+ #include "uuid.h"
+ #include "uuid_vers.h"
+ #include "uuid_md5.h"
+ #include "uuid_prng.h"
+ #include "uuid_mac.h"
+ #include "uuid_ui64.h"
+ #include "uuid_str.h"
+ #include "uuid_bm.h"
+ #include "uuid_ac.h"
+
+ /* maximum number of 100ns ticks of the actual resolution of system clock
+ (which in our case is 1us (= 1000ns) because we use gettimeofday(2) */
+ #define UUIDS_PER_TICK 10
+
+ /* time offset between UUID and Unix Epoch time according to standards.
+ (UUID UTC base time is October 15, 1582
+ Unix UTC base time is January 1, 1970) */
+ #define UUID_TIMEOFFSET "01B21DD213814000"
+
+ /* IEEE 802 MAC address encoding/decoding bit fields
+
+ ATTENTION:
+
+ In case no real/physical IEEE 802 address is available, both
+ "draft-leach-uuids-guids-01" (section "4. Node IDs when no IEEE 802
+ network card is available") and RFC 2518 (section "6.4.1 Node Field
+ Generation Without the IEEE 802 Address") recommend (quoted from RFC
+ 2518):
+
+ "The ideal solution is to obtain a 47 bit cryptographic quality
+ random number, and use it as the low 47 bits of the node ID, with
+ the most significant bit of the first octet of the node ID set to
+ 1. This bit is the unicast/multicast bit, which will never be set
+ in IEEE 802 addresses obtained from network cards; hence, there can
+ never be a conflict between UUIDs generated by machines with and
+ without network cards."
+
+ This passage clearly explains the intention to use IEEE 802 multicast
+ addresses. Unfortunately, it incorrectly explains how to implement
+ this! It should instead specify the "*LEAST* significant bit of the
+ first octet of the node ID" as the multicast bit in a memory and
+ hexadecimal string representation of a 48-bit IEEE 802 MAC address.
+
+ Unfortunately, even the reference implementation included in the
+ expired IETF "draft-leach-uuids-guids-01" incorrectly set the
+ multicast bit with an OR bit operation and an incorrect mask of
+ 0x80. Hence, several other UUID implementations found on the
+ Internet have inherited this bug.
+
+ Luckily, neither DCE 1.1 nor ISO/IEC 11578:1996 are affected by this
+ problem. They disregard the topic of missing IEEE 802 addresses
+ entirely, and thus avoid adopting this bug from the original draft
+ and code ;-)
+
+ It seems that this standards bug arises from a false interpretation,
+ as the multicast bit is actually is the *MOST* significant bit in
+ IEEE 802.3 (Ethernet) _transmission order_ of an IEEE 802 MAC address.
+ The authors were likely not aware that the bitwise order of an octet
+ from a MAC address memory and hexadecimal string representation is
+ still always from left (MSB, bit 7) to right (LSB, bit 0).
+
+ For more information, see "Understanding Physical Addresses" in
+ "Ethernet -- The Definitive Guide", p.43, and the section "ETHERNET
+ MULTICAST ADDRESSES" in http://www.iana.org/assignments/ethernet-numbers.
+
+ At OSSP, we do it the intended/correct way and generate a real IEEE 802
+ multicast address. Those wanting to encode broken IEEE 802 MAC addresses
+ (as specified) can nevertheless use a brain dead compile-time option
+ to switch off the correct behavior. When decoding we always use the
+ correct behavior of course. */
+
+ /* encoding */
+ #ifdef WITH_RFC2518
+ #define IEEE_MAC_MCBIT_ENC BM_OCTET(1,0,0,0,0,0,0,0)
+ #else
+ #define IEEE_MAC_MCBIT_ENC BM_OCTET(0,0,0,0,0,0,0,1)
+ #endif
+ #define IEEE_MAC_LOBIT_ENC BM_OCTET(0,0,0,0,0,0,1,0)
+
+ /* decoding */
+ #define IEEE_MAC_MCBIT_DEC BM_OCTET(0,0,0,0,0,0,0,1)
+ #define IEEE_MAC_LOBIT_DEC BM_OCTET(0,0,0,0,0,0,1,0)
+
+ /* IEEE 802 MAC address octet length */
+ #define IEEE_MAC_OCTETS 6
+
+ /* UUID binary representation according to UUID standards */
+ typedef struct {
+ uuid_uint32_t time_low; /* bits 0-31 of time field */
+ uuid_uint16_t time_mid; /* bits 32-47 of time field */
+ uuid_uint16_t time_hi_and_version; /* bits 48-59 of time field plus 4 bit version */
+ uuid_uint8_t clock_seq_hi_and_reserved; /* bits 8-13 of clock sequence field plus 2 bit variant */
+ uuid_uint8_t clock_seq_low; /* bits 0-7 of clock sequence field */
+ uuid_uint8_t node[IEEE_MAC_OCTETS]; /* bits 0-47 of node MAC address */
+ } 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 */
+ uuid_uint8_t mac[IEEE_MAC_OCTETS]; /* pre-determined MAC address */
+ struct timeval time_last; /* last retrieved timestamp */
+ unsigned long time_seq; /* last timestamp sequence counter */
+ };
+
+ /* 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_load(*uuid, "nil");
+
+ /* 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;
+
+ /* resolve MAC address for insertion into node field of UUIDs */
+ if (!mac_address((unsigned char *)((*uuid)->mac), sizeof((*uuid)->mac))) {
+ memset((*uuid)->mac, '\0', sizeof((*uuid)->mac));
+ (*uuid)->mac[0] = BM_OCTET(1,0,0,0,0,0,0,0);
+ }
+
+ /* initialize time attributes */
+ (*uuid)->time_last.tv_sec = 0;
+ (*uuid)->time_last.tv_usec = 0;
+ (*uuid)->time_seq = 0;
+
+ 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;
+ }
+
+ /* 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 = UUID_TRUE;
+ for (i = 0, ucp = (unsigned char *)&(uuid->obj); i < UUID_LEN_BIN; i++) {
+ if (*ucp++ != '\0') {
+ *result = UUID_FALSE;
+ break;
+ }
+ }
+
+ 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;
+ }
+
+ /* INTERNAL: unpack UUID binary presentation into UUID object
+ (allows in-place operation for internal efficiency!) */
+ static uuid_rc_t uuid_import_bin(uuid_t *uuid, const void *data_ptr, size_t data_len)
+ {
+ const uuid_uint8_t *in;
+ uuid_uint32_t tmp32;
+ uuid_uint16_t tmp16;
+ unsigned int i;
+
+ /* sanity check argument(s) */
+ if (uuid == NULL || data_ptr == NULL || data_len < UUID_LEN_BIN)
+ return UUID_RC_ARG;
+
+ /* treat input data buffer as octet stream */
+ in = (const uuid_uint8_t *)data_ptr;
+
+ /* 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;
+ }
+
+ /* INTERNAL: pack UUID object into binary representation
+ (allows in-place operation for internal efficiency!) */
+ static uuid_rc_t uuid_export_bin(uuid_t *uuid, void **data_ptr, size_t *data_len)
+ {
+ uuid_uint8_t *out;
+ uuid_uint32_t tmp32;
+ uuid_uint16_t tmp16;
+ unsigned int i;
+
+ /* sanity check argument(s) */
+ if (uuid == NULL || data_ptr == NULL)
+ return UUID_RC_ARG;
+
+ /* optionally allocate octet data buffer */
+ if (*data_ptr == NULL) {
+ if ((*data_ptr = malloc(sizeof(uuid_t))) == NULL)
+ return UUID_RC_MEM;
+ if (data_len != NULL)
+ *data_len = UUID_LEN_BIN;
+ }
+ else {
+ if (data_len == NULL)
+ return UUID_RC_ARG;
+ if (*data_len < UUID_LEN_BIN)
+ return UUID_RC_MEM;
+ *data_len = UUID_LEN_BIN;
+ }
+
+ /* treat output data buffer as octet stream */
+ out = (uuid_uint8_t *)(*data_ptr);
+
+ /* 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, size_t str_len)
+ {
+ int i;
+ const char *cp;
+
+ /* example reference:
+ f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+ 012345678901234567890123456789012345
+ 0 1 2 3 */
+ if (str == NULL)
+ return UUID_FALSE;
+ if (str_len == 0)
+ str_len = strlen(str);
+ if (str_len < UUID_LEN_STR)
+ return UUID_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 UUID_FALSE;
+ }
+ if (!isxdigit((int)(*cp)))
+ return UUID_FALSE;
+ }
+ return UUID_TRUE;
+ }
+
+ /* INTERNAL: import UUID object from string representation */
+ static uuid_rc_t uuid_import_str(uuid_t *uuid, const void *data_ptr, size_t data_len)
+ {
+ uuid_uint16_t tmp16;
+ const char *cp;
+ char hexbuf[3];
+ const char *str;
+ unsigned int i;
+
+ /* sanity check argument(s) */
+ if (uuid == NULL || data_ptr == NULL || data_len < UUID_LEN_STR)
+ return UUID_RC_ARG;
+
+ /* check for correct UUID string representation syntax */
+ str = (const char *)data_ptr;
+ if (!uuid_isstr(str, 0))
+ 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;
+ }
+
+ /* INTERNAL: export UUID object to string representation */
+ static uuid_rc_t uuid_export_str(uuid_t *uuid, void **data_ptr, size_t *data_len)
+ {
+ /* sanity check argument(s) */
+ if (uuid == NULL || data_ptr == NULL)
+ return UUID_RC_ARG;
+
+ /* allocate output buffer */
+ if (*data_ptr == NULL) {
+ if ((*data_ptr = (void *)malloc(UUID_LEN_STR+1)) == NULL)
+ return UUID_RC_MEM;
+ if (data_len != NULL)
+ *data_len = UUID_LEN_STR+1;
+ }
+ else {
+ if (data_len == NULL)
+ return UUID_RC_ARG;
+ if (*data_len < UUID_LEN_STR+1)
+ return UUID_RC_MEM;
+ *data_len = UUID_LEN_STR+1;
+ }
+
+ /* format UUID into string representation */
+ str_snprintf((char *)(*data_ptr), UUID_LEN_STR+1,
+ "%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;
+ }
+
+ /* decoding tables */
+ static struct {
+ uuid_uint8_t num;
+ const char *desc;
+ } uuid_dectab_variant[] = {
+ { BM_OCTET(0,0,0,0,0,0,0,0), "reserved (NCS backward compatible)" },
+ { BM_OCTET(1,0,0,0,0,0,0,0), "DCE 1.1, ISO/IEC 11578:1996" },
+ { BM_OCTET(1,1,0,0,0,0,0,0), "reserved (Microsoft GUID)" },
+ { BM_OCTET(1,1,1,0,0,0,0,0), "reserved (future use)" }
+ };
+ static struct {
+ int num;
+ const char *desc;
+ } uuid_dectab_version[] = {
+ { 1, "time and node based" },
+ { 3, "name based" },
+ { 4, "random data based" }
+ };
+
+ /* INTERNAL: dump UUID object as descriptive text */
+ static uuid_rc_t uuid_export_txt(uuid_t *uuid, void **data_ptr, size_t *data_len)
+ {
+ uuid_rc_t rc;
+ char **out;
+ char *out_ptr;
+ size_t out_len;
+ const char *version;
+ const char *variant;
+ char *content;
+ int isnil;
+ uuid_uint8_t tmp8;
+ uuid_uint16_t tmp16;
+ uuid_uint32_t tmp32;
+ uuid_uint8_t tmp_bin[UUID_LEN_BIN];
+ char tmp_str[UUID_LEN_STR+1];
+ void *tmp_ptr;
+ size_t tmp_len;
+ ui64_t t;
+ ui64_t t_offset;
+ int t_nsec;
+ int t_usec;
+ time_t t_sec;
+ char t_buf[19+1]; /* YYYY-MM-DD HH:MM:SS */
+ struct tm *tm;
+ unsigned int i;
+
+ /* sanity check argument(s) */
+ if (uuid == NULL || data_ptr == NULL)
+ return UUID_RC_ARG;
+
+ /* initialize output buffer */
+ out_ptr = NULL;
+ out = &out_ptr;
+
+ /* check for special case of "Nil UUID" */
+ if ((rc = uuid_isnil(uuid, &isnil)) != UUID_RC_OK)
+ return rc;
+
+ /* decode into string representation */
+ tmp_ptr = (void *)&tmp_str;
+ tmp_len = sizeof(tmp_str);
+ if ((rc = uuid_export(uuid, UUID_FMT_STR, &tmp_ptr, &tmp_len)) != UUID_RC_OK)
+ return rc;
+ str_rsprintf(out, "UUID: %s\n", tmp_str);
+
+ /* decode UUID variant */
+ tmp8 = uuid->obj.clock_seq_hi_and_reserved;
+ if (isnil)
+ variant = "n.a.";
+ else {
+ variant = "unknown";
+ for (i = 7; i >= 0; i--) {
+ if ((tmp8 & BM_BIT(i,1)) == 0) {
+ tmp8 &= ~BM_MASK(i,0);
+ break;
+ }
+ }
+ for (i = 0; i < sizeof(uuid_dectab_variant)/sizeof(uuid_dectab_variant[0]); i++) {
+ if (uuid_dectab_variant[i].num == tmp8) {
+ variant = uuid_dectab_variant[i].desc;
+ break;
+ }
+ }
+ }
+ str_rsprintf(out, "variant: %s\n", variant);
+
+ /* decode UUID version */
+ tmp16 = (BM_SHR(uuid->obj.time_hi_and_version, 12) & BM_MASK(3,0));
+ if (isnil)
+ version = "n.a.";
+ else {
+ version = "unknown";
+ for (i = 0; i < sizeof(uuid_dectab_version)/sizeof(uuid_dectab_version[0]); i++) {
+ if (uuid_dectab_version[i].num == (int)tmp16) {
+ version = uuid_dectab_version[i].desc;
+ break;
+ }
+ }
+ }
+ str_rsprintf(out, "version: %d (%s)\n", (int)tmp16, version);
+
+ /*
+ * decode UUID content
+ */
+
+ if (tmp8 == BM_OCTET(1,0,0,0,0,0,0,0) && tmp16 == 1) {
+ /* decode DCE 1.1 version 1 UUID */
+
+ /* decode system time */
+ t = ui64_rol(ui64_n2i((unsigned long)(uuid->obj.time_hi_and_version & BM_MASK(11,0))), 48, NULL),
+ t = ui64_or(t, ui64_rol(ui64_n2i((unsigned long)(uuid->obj.time_mid)), 32, NULL));
+ t = ui64_or(t, ui64_n2i((unsigned long)(uuid->obj.time_low)));
+ t_offset = ui64_s2i(UUID_TIMEOFFSET, NULL, 16);
+ t = ui64_sub(t, t_offset, NULL);
+ t = ui64_divn(t, 10, &t_nsec);
+ t = ui64_divn(t, 1000000, &t_usec);
+ t_sec = (time_t)ui64_i2n(t);
+ tm = gmtime(&t_sec);
+ strftime(t_buf, sizeof(t_buf), "%Y-%m-%d %H:%M:%S", tm);
+ str_rsprintf(out, "content: time: %s.%06d.%d UTC\n", t_buf, t_usec, t_nsec);
+
+ /* decode clock sequence */
+ tmp32 = ((uuid->obj.clock_seq_hi_and_reserved & BM_MASK(5,0)) << 8)
+ + uuid->obj.clock_seq_low;
+ str_rsprintf(out, " clock: %ld (usually random)\n", (long)tmp32);
+
+ /* decode node MAC address */
+ str_rsprintf(out, " node: %02x:%02x:%02x:%02x:%02x:%02x (%s %s)\n",
+ (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],
+ (uuid->obj.node[0] & IEEE_MAC_LOBIT_DEC ? "local" : "global"),
+ (uuid->obj.node[0] & IEEE_MAC_MCBIT_DEC ? "multicast" : "unicast"));
+ }
+ else {
+ /* decode anything else as hexadecimal byte-string only */
+
+ /* determine annotational hint */
+ content = "not decipherable, because unknown UUID version";
+ if (isnil)
+ content = "special case of DCE 1.1 Nil UUID";
+ else if (tmp16 == 3)
+ content = "not decipherable, because message digest only";
+ else if (tmp16 == 4)
+ content = "no semantics, because random data only";
+
+ /* pack UUID into binary representation */
+ tmp_ptr = (void *)&tmp_bin;
+ tmp_len = sizeof(tmp_bin);
+ if ((rc = uuid_export(uuid, UUID_FMT_BIN, &tmp_ptr, &tmp_len)) != UUID_RC_OK)
+ return rc;
+
+ /* mask out version and variant parts */
+ tmp_bin[6] &= BM_MASK(3,0);
+ tmp_bin[8] &= BM_MASK(5,0);
+
+ /* dump as colon-seperated hexadecimal byte-string */
+ str_rsprintf(out,
+ "content: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n"
+ " (%s)\n",
+ (unsigned int)tmp_bin[0], (unsigned int)tmp_bin[1], (unsigned int)tmp_bin[2],
+ (unsigned int)tmp_bin[3], (unsigned int)tmp_bin[4], (unsigned int)tmp_bin[5],
+ (unsigned int)tmp_bin[6], (unsigned int)tmp_bin[7], (unsigned int)tmp_bin[8],
+ (unsigned int)tmp_bin[9], (unsigned int)tmp_bin[10], (unsigned int)tmp_bin[11],
+ (unsigned int)tmp_bin[12], (unsigned int)tmp_bin[13], (unsigned int)tmp_bin[14],
+ (unsigned int)tmp_bin[15], content);
+ }
+
+ /* provide result */
+ out_len = strlen(out_ptr)+1;
+ if (*data_ptr == NULL) {
+ *data_ptr = (void *)out_ptr;
+ if (data_len != NULL)
+ *data_len = out_len;
+ }
+ else {
+ if (data_len == NULL)
+ return UUID_RC_ARG;
+ if (*data_len < out_len)
+ return UUID_RC_MEM;
+ memcpy(*data_ptr, &out_ptr, out_len);
+ }
+
+ return UUID_RC_OK;
+ }
+
+ /* UUID importing */
+ uuid_rc_t uuid_import(uuid_t *uuid, uuid_fmt_t fmt, const void *data_ptr, size_t data_len)
+ {
+ uuid_rc_t rc;
+
+ /* sanity check argument(s) */
+ if (uuid == NULL || data_ptr == NULL)
+ return UUID_RC_ARG;
+
+ /* dispatch into format-specific functions */
+ switch (fmt) {
+ case UUID_FMT_BIN: rc = uuid_import_bin(uuid, data_ptr, data_len); break;
+ case UUID_FMT_STR: rc = uuid_import_str(uuid, data_ptr, data_len); break;
+ case UUID_FMT_TXT: rc = UUID_RC_IMP; /* not implemented */ break;
+ default: rc = UUID_RC_ARG;
+ }
+
+ return rc;
+ }
+
+ /* UUID exporting */
+ uuid_rc_t uuid_export(uuid_t *uuid, uuid_fmt_t fmt, void **data_ptr, size_t *data_len)
+ {
+ uuid_rc_t rc;
+
+ /* sanity check argument(s) */
+ if (uuid == NULL || data_ptr == NULL)
+ return UUID_RC_ARG;
+
+ /* dispatch into format-specific functions */
+ switch (fmt) {
+ case UUID_FMT_BIN: rc = uuid_export_bin(uuid, data_ptr, data_len); break;
+ case UUID_FMT_STR: rc = uuid_export_str(uuid, data_ptr, data_len); break;
+ case UUID_FMT_TXT: rc = uuid_export_txt(uuid, data_ptr, data_len); break;
+ default: rc = UUID_RC_ARG;
+ }
+
+ return rc;
+ }
+
+ /* 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 &= BM_MASK(11,0);
+ uuid->obj.time_hi_and_version |= BM_SHL((uuid_uint16_t)version, 12);
+
+ /* set variant (always DCE 1.1 only) */
+ uuid->obj.clock_seq_hi_and_reserved &= BM_MASK(5,0);
+ uuid->obj.clock_seq_hi_and_reserved |= BM_SHL(0x02, 6);
+ return;
+ }
+
+ /* INTERNAL: generate UUID version 1: time, clock and node based */
+ static uuid_rc_t uuid_make_v1(uuid_t *uuid, unsigned int mode, va_list ap)
+ {
+ struct timeval time_now;
+ #ifdef HAVE_NANOSLEEP
+ struct timespec ts;
+ #else
+ struct timeval tv;
+ #endif
+ ui64_t t;
+ ui64_t offset;
+ ui64_t ov;
+ uuid_uint16_t clck;
+
+ /*
+ * GENERATE TIME
+ */
+
+ /* determine current system time and sequence counter */
+ while (1) {
+ /* determine current system time */
+ if (gettimeofday(&time_now, NULL) == -1)
+ return UUID_RC_SYS;
+
+ /* check whether system time changed since last retrieve */
+ if (!( time_now.tv_sec == uuid->time_last.tv_sec
+ && time_now.tv_usec == uuid->time_last.tv_usec))
+ /* reset time sequence counter */
+ uuid->time_seq = 0;
+
+ /* until we are out of UUIDs per tick, increment
+ the time/tick sequence counter and continue */
+ if (uuid->time_seq < UUIDS_PER_TICK) {
+ uuid->time_seq++;
+ break;
+ }
+
+ /* stall the UUID generation until the system clock (which
+ has a gettimeofday(2) resolution of 1us) catches up */
+ #ifdef HAVE_NANOSLEEP
+ /* sleep for 500ns (1/2us) */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 500;
+ nanosleep(&ts, NULL);
+ #else
+ /* sleep for 1000ns (1us) */
+ tv.tv_sec = 0;
+ tv.tv_usec = 1;
+ select(0, NULL, NULL, NULL, &tv);
+ #endif
+ }
+
+ /* convert from timeval (sec,usec) to OSSP ui64 (100*nsec) format */
+ t = ui64_n2i(time_now.tv_sec);
+ t = ui64_muln(t, 1000000, NULL);
+ t = ui64_addn(t, time_now.tv_usec, NULL);
+ t = ui64_muln(t, 10, NULL);
+
+ /* adjust for offset between UUID and Unix Epoch time */
+ offset = ui64_s2i(UUID_TIMEOFFSET, NULL, 16);
+ t = ui64_add(t, offset, NULL);
+
+ /* compensate for low resolution system clock by adding
+ the time/tick sequence counter */
+ if (uuid->time_seq > 0)
+ t = ui64_addn(t, uuid->time_seq, 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 */
+
+ /*
+ * GENERATE CLOCK
+ */
+
+ /* retrieve current clock sequence */
+ clck = ((uuid->obj.clock_seq_hi_and_reserved & BM_MASK(5,0)) << 8)
+ + uuid->obj.clock_seq_low;
+
+ /* generate new random clock sequence (initially or if the
+ time has stepped backwards) or else just increase it */
+ if ( clck == 0
+ || ( time_now.tv_sec < uuid->time_last.tv_sec
+ || ( time_now.tv_sec == uuid->time_last.tv_sec
+ && time_now.tv_usec < uuid->time_last.tv_usec)))
+ prng_data(uuid->prng, (void *)&clck, sizeof(clck));
+ else
+ clck++;
+ clck %= BM_POW2(14);
+
+ /* store back new clock sequence */
+ uuid->obj.clock_seq_hi_and_reserved =
+ (uuid->obj.clock_seq_hi_and_reserved & BM_MASK(7,6))
+ | (uuid_uint8_t)((clck >> 8) & 0xff);
+ uuid->obj.clock_seq_low =
+ (uuid_uint8_t)(clck & 0xff);
+
+ /*
+ * GENERATE NODE
+ */
+
+ if ((mode & UUID_MCASTRND) || (uuid->mac[0] & BM_OCTET(1,0,0,0,0,0,0,0))) {
+ /* generate random IEEE 802 local multicast MAC address */
+ prng_data(uuid->prng, (void *)&(uuid->obj.node), sizeof(uuid->obj.node));
+ uuid->obj.node[0] |= IEEE_MAC_MCBIT_ENC;
+ uuid->obj.node[0] |= IEEE_MAC_LOBIT_ENC;
+ }
+ else {
+ /* use real regular MAC address */
+ memcpy(uuid->obj.node, uuid->mac, sizeof(uuid->mac));
+ }
+
+ /*
+ * FINISH
+ */
+
+ /* remember current system time for next iteration */
+ uuid->time_last.tv_sec = time_now.tv_sec;
+ uuid->time_last.tv_usec = time_now.tv_usec;
+
+ /* brand with version and variant */
+ uuid_brand(uuid, 1);
+
+ return UUID_RC_OK;
+ }
+
+ /* INTERNAL: pre-defined UUID values.
+ (defined as network byte ordered octet stream) */
+ static struct {
+ char *name;
+ uuid_uint8_t uuid[UUID_LEN_BIN];
+ } uuid_value_table[] = {
+ { "nil", /* 00000000-0000-0000-0000-000000000000 ("Nil UUID") */
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } },
+ { "ns:DNS", /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 (see draft-leach-uuids-guids-01.txt) */
+ { 0x6b,0xa7,0xb8,0x10,0x9d,0xad,0x11,0xd1,0x80,0xb4,0x00,0xc0,0x4f,0xd4,0x30,0xc8 } },
+ { "ns:URL", /* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 (see draft-leach-uuids-guids-01.txt) */
+ { 0x6b,0xa7,0xb8,0x11,0x9d,0xad,0x11,0xd1,0x80,0xb4,0x00,0xc0,0x4f,0xd4,0x30,0xc8 } },
+ { "ns:OID", /* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 (see draft-leach-uuids-guids-01.txt) */
+ { 0x6b,0xa7,0xb8,0x12,0x9d,0xad,0x11,0xd1,0x80,0xb4,0x00,0xc0,0x4f,0xd4,0x30,0xc8 } },
+ { "ns:X500", /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 (see draft-leach-uuids-guids-01.txt) */
+ { 0x6b,0xa7,0xb8,0x14,0x9d,0xad,0x11,0xd1,0x80,0xb4,0x00,0xc0,0x4f,0xd4,0x30,0xc8 } }
+ };
+
+ /* load UUID object with pre-defined value */
+ uuid_rc_t uuid_load(uuid_t *uuid, const char *name)
+ {
+ uuid_uint8_t *uuid_octets;
+ uuid_rc_t rc;
+ unsigned int i;
+
+ /* sanity check argument(s) */
+ if (uuid == NULL || name == NULL)
+ return UUID_RC_ARG;
+
+ /* search for UUID in table */
+ uuid_octets = NULL;
+ for (i = 0; i < sizeof(uuid_value_table)/sizeof(uuid_value_table[0]); i++) {
+ if (strcmp(uuid_value_table[i].name, name) == 0) {
+ uuid_octets = uuid_value_table[i].uuid;
+ break;
+ }
+ }
+ if (uuid_octets == NULL)
+ return UUID_RC_ARG;
+
+ /* import value into UUID object */
+ if ((rc = uuid_import(uuid, UUID_FMT_BIN, uuid_octets, UUID_LEN_BIN)) != UUID_RC_OK)
+ return rc;
+
+ return UUID_RC_OK;
+ }
+
+ /* INTERNAL: generate UUID version 3: name based */
+ static uuid_rc_t uuid_make_v3(uuid_t *uuid, unsigned int mode, va_list ap)
+ {
+ char *str;
+ uuid_t *uuid_ns;
+ uuid_uint8_t uuid_buf[UUID_LEN_BIN];
+ void *uuid_ptr;
+ size_t uuid_len;
+
+ /* determine namespace UUID and name string arguments */
+ if ((uuid_ns = (uuid_t *)va_arg(ap, void *)) == 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 */
+ uuid_ptr = (void *)&uuid_buf;
+ uuid_len = sizeof(uuid_buf);
+ uuid_export(uuid_ns, UUID_FMT_BIN, &uuid_ptr, &uuid_len);
+ md5_update(uuid->md5, uuid_buf, uuid_len);
+
+ /* 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_ptr = (void *)&(uuid->obj);
+ md5_store(uuid->md5, &uuid_ptr, NULL);
+
+ /* fulfill requirement of standard and convert UUID data into
+ local/host byte order (this uses fact that uuid_import_bin() is
+ able to operate in-place!) */
+ uuid_import(uuid, UUID_FMT_BIN, (void *)&(uuid->obj), UUID_LEN_BIN);
+
+ /* 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_make_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_make(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_make_v1(uuid, mode, ap);
+ else if (mode & UUID_VERSION3)
+ rc = uuid_make_v3(uuid, mode, ap);
+ else if (mode & UUID_VERSION4)
+ rc = uuid_make_v4(uuid, mode, ap);
+ else
+ rc = UUID_RC_ARG;
+ va_end(ap);
+
+ return rc;
+ }
+
+ /* 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;
+ }
+
+ /* OSSP uuid version (link-time information) */
+ unsigned long uuid_version(void)
+ {
+ return (unsigned long)(_UUID_VERSION);
+ }
+
|