OSSP CVS Repository

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

ossp-pkg/uuid/uuid.c 1.11
/*
**  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
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>

#include "config.h"
#include "uuid.h"
#include "uuid_md5.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;
}

/* check for correct 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;
}

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->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);

    /* parse hex values of "clock" parts */
    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);

    /* parse hex values of "node" part */
    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;
}

/*
 * 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)
 */
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 } }
};

/* 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_name;
    void *ns_uuid;
    uuid_t *uuid_tmp;
    uuid_rc_t rc;
    md5_t *md5;
    int i;

    /* determine namespace UUID name and argument name string */
    if ((ns_name = (char *)va_arg(ap, char *)) == NULL)
        return UUID_RC_ARG;
    if ((str = (char *)va_arg(ap, char *)) == NULL)
        return UUID_RC_ARG;

    /* create MD5 context */
    if (md5_create(&md5) != MD5_RC_OK)
        return UUID_RC_MEM;

    /* load the namespace UUID into MD5 context */
    if (uuid_isstr(ns_name)) {
        /* custom namespace via UUID string representation */
        if ((rc = uuid_create(&uuid_tmp)) != UUID_RC_OK)
            return rc;
        if ((rc = uuid_parse(uuid_tmp, ns_name)) != UUID_RC_OK)
            return rc;
        uuid_pack(uuid_tmp, (void **)&uuid_tmp);
        md5_update(md5, (void *)uuid_tmp, UUID_LEN_BIN);
        uuid_destroy(uuid_tmp);
    }
    else {
        /* standard namespace via UUID namespace id */
        ns_uuid = NULL;
        for (i = 0; i < sizeof(uuid_ns_table)/sizeof(uuid_ns_table[0]); i++) {
             if (strcmp(uuid_ns_table[i].name, ns_name) == 0) {
                 ns_uuid = uuid_ns_table[i].uuid;
                 break;
             }
        }
        if (ns_uuid == NULL)
            return UUID_RC_ARG;
        md5_update(md5, ns_uuid, UUID_LEN_BIN);
    }

    /* load the argument name string into MD5 context */
    md5_update(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!) */
    md5_store(md5, (void *)&uuid, NULL);

    /* destroy MD5 context */
    md5_destroy(md5);

    /* 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);

    /* brand UUID with version and variant */
    uuid_brand(uuid, 3);

    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 UUID with random data */
    uuid_prng_getdata((void *)uuid, sizeof(uuid_t));

    /* brand UUID 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;
}


CVSTrac 2.0.1