OSSP CVS Repository

ossp - ossp-pkg/uuid/uuid.c 1.44
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/uuid/uuid.c 1.44
/*
**  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 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 and continue */
            uuid->time_seq = 0;
            break;
        }

        /* 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_MAKE_MC) || (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_MAKE_V1)
        rc = uuid_make_v1(uuid, mode, ap);
    else if (mode & UUID_MAKE_V3)
        rc = uuid_make_v3(uuid, mode, ap);
    else if (mode & UUID_MAKE_V4)
        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;
        case UUID_RC_INT: str = "internal error";   break;
        case UUID_RC_IMP: str = "not implemented";  break;
        default:          str = NULL;               break;
    }
    return str;
}

/* OSSP uuid version (link-time information) */
unsigned long uuid_version(void)
{
    return (unsigned long)(_UUID_VERSION);
}


CVSTrac 2.0.1