summaryrefslogtreecommitdiffstats
path: root/contrib/uuid-ossp/uuid-ossp.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/uuid-ossp/uuid-ossp.c')
-rw-r--r--contrib/uuid-ossp/uuid-ossp.c553
1 files changed, 553 insertions, 0 deletions
diff --git a/contrib/uuid-ossp/uuid-ossp.c b/contrib/uuid-ossp/uuid-ossp.c
new file mode 100644
index 0000000..456a438
--- /dev/null
+++ b/contrib/uuid-ossp/uuid-ossp.c
@@ -0,0 +1,553 @@
+/*-------------------------------------------------------------------------
+ *
+ * UUID generation functions using the BSD, E2FS or OSSP UUID library
+ *
+ * Copyright (c) 2007-2022, PostgreSQL Global Development Group
+ *
+ * Portions Copyright (c) 2009 Andrew Gierth
+ *
+ * contrib/uuid-ossp/uuid-ossp.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "common/cryptohash.h"
+#include "common/sha1.h"
+#include "port/pg_bswap.h"
+#include "utils/builtins.h"
+#include "utils/uuid.h"
+
+/*
+ * It's possible that there's more than one uuid.h header file present.
+ * We expect configure to set the HAVE_ symbol for only the one we want.
+ *
+ * BSD includes a uuid_hash() function that conflicts with the one in
+ * builtins.h; we #define it out of the way.
+ */
+#define uuid_hash bsd_uuid_hash
+
+#if defined(HAVE_UUID_H)
+#include <uuid.h>
+#elif defined(HAVE_OSSP_UUID_H)
+#include <ossp/uuid.h>
+#elif defined(HAVE_UUID_UUID_H)
+#include <uuid/uuid.h>
+#else
+#error "please use configure's --with-uuid switch to select a UUID library"
+#endif
+
+#undef uuid_hash
+
+/* Check our UUID length against OSSP's; better both be 16 */
+#if defined(HAVE_UUID_OSSP) && (UUID_LEN != UUID_LEN_BIN)
+#error UUID length mismatch
+#endif
+
+/* Define some constants like OSSP's, to make the code more readable */
+#ifndef HAVE_UUID_OSSP
+#define UUID_MAKE_MC 0
+#define UUID_MAKE_V1 1
+#define UUID_MAKE_V2 2
+#define UUID_MAKE_V3 3
+#define UUID_MAKE_V4 4
+#define UUID_MAKE_V5 5
+#endif
+
+/*
+ * A DCE 1.1 compatible source representation of UUIDs, derived from
+ * the BSD implementation. BSD already has this; OSSP doesn't need it.
+ */
+#ifdef HAVE_UUID_E2FS
+typedef struct
+{
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_hi_and_reserved;
+ uint8_t clock_seq_low;
+ uint8_t node[6];
+} dce_uuid_t;
+#else
+#define dce_uuid_t uuid_t
+#endif
+
+/* If not OSSP, we need some endianness-manipulation macros */
+#ifndef HAVE_UUID_OSSP
+
+#define UUID_TO_NETWORK(uu) \
+do { \
+ uu.time_low = pg_hton32(uu.time_low); \
+ uu.time_mid = pg_hton16(uu.time_mid); \
+ uu.time_hi_and_version = pg_hton16(uu.time_hi_and_version); \
+} while (0)
+
+#define UUID_TO_LOCAL(uu) \
+do { \
+ uu.time_low = pg_ntoh32(uu.time_low); \
+ uu.time_mid = pg_ntoh16(uu.time_mid); \
+ uu.time_hi_and_version = pg_ntoh16(uu.time_hi_and_version); \
+} while (0)
+
+#define UUID_V3_OR_V5(uu, v) \
+do { \
+ uu.time_hi_and_version &= 0x0FFF; \
+ uu.time_hi_and_version |= (v << 12); \
+ uu.clock_seq_hi_and_reserved &= 0x3F; \
+ uu.clock_seq_hi_and_reserved |= 0x80; \
+} while(0)
+
+#endif /* !HAVE_UUID_OSSP */
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(uuid_nil);
+PG_FUNCTION_INFO_V1(uuid_ns_dns);
+PG_FUNCTION_INFO_V1(uuid_ns_url);
+PG_FUNCTION_INFO_V1(uuid_ns_oid);
+PG_FUNCTION_INFO_V1(uuid_ns_x500);
+
+PG_FUNCTION_INFO_V1(uuid_generate_v1);
+PG_FUNCTION_INFO_V1(uuid_generate_v1mc);
+PG_FUNCTION_INFO_V1(uuid_generate_v3);
+PG_FUNCTION_INFO_V1(uuid_generate_v4);
+PG_FUNCTION_INFO_V1(uuid_generate_v5);
+
+#ifdef HAVE_UUID_OSSP
+
+static void
+pguuid_complain(uuid_rc_t rc)
+{
+ char *err = uuid_error(rc);
+
+ if (err != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+ errmsg("OSSP uuid library failure: %s", err)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+ errmsg("OSSP uuid library failure: error code %d", rc)));
+}
+
+/*
+ * We create a uuid_t object just once per session and re-use it for all
+ * operations in this module. OSSP UUID caches the system MAC address and
+ * other state in this object. Reusing the object has a number of benefits:
+ * saving the cycles needed to fetch the system MAC address over and over,
+ * reducing the amount of entropy we draw from /dev/urandom, and providing a
+ * positive guarantee that successive generated V1-style UUIDs don't collide.
+ * (On a machine fast enough to generate multiple UUIDs per microsecond,
+ * or whatever the system's wall-clock resolution is, we'd otherwise risk
+ * collisions whenever random initialization of the uuid_t's clock sequence
+ * value chanced to produce duplicates.)
+ *
+ * However: when we're doing V3 or V5 UUID creation, uuid_make needs two
+ * uuid_t objects, one holding the namespace UUID and one for the result.
+ * It's unspecified whether it's safe to use the same uuid_t for both cases,
+ * so let's cache a second uuid_t for use as the namespace holder object.
+ */
+static uuid_t *
+get_cached_uuid_t(int which)
+{
+ static uuid_t *cached_uuid[2] = {NULL, NULL};
+
+ if (cached_uuid[which] == NULL)
+ {
+ uuid_rc_t rc;
+
+ rc = uuid_create(&cached_uuid[which]);
+ if (rc != UUID_RC_OK)
+ {
+ cached_uuid[which] = NULL;
+ pguuid_complain(rc);
+ }
+ }
+ return cached_uuid[which];
+}
+
+static char *
+uuid_to_string(const uuid_t *uuid)
+{
+ char *buf = palloc(UUID_LEN_STR + 1);
+ void *ptr = buf;
+ size_t len = UUID_LEN_STR + 1;
+ uuid_rc_t rc;
+
+ rc = uuid_export(uuid, UUID_FMT_STR, &ptr, &len);
+ if (rc != UUID_RC_OK)
+ pguuid_complain(rc);
+
+ return buf;
+}
+
+
+static void
+string_to_uuid(const char *str, uuid_t *uuid)
+{
+ uuid_rc_t rc;
+
+ rc = uuid_import(uuid, UUID_FMT_STR, str, UUID_LEN_STR + 1);
+ if (rc != UUID_RC_OK)
+ pguuid_complain(rc);
+}
+
+
+static Datum
+special_uuid_value(const char *name)
+{
+ uuid_t *uuid = get_cached_uuid_t(0);
+ char *str;
+ uuid_rc_t rc;
+
+ rc = uuid_load(uuid, name);
+ if (rc != UUID_RC_OK)
+ pguuid_complain(rc);
+ str = uuid_to_string(uuid);
+
+ return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
+}
+
+/* len is unused with OSSP, but we want to have the same number of args */
+static Datum
+uuid_generate_internal(int mode, const uuid_t *ns, const char *name, int len)
+{
+ uuid_t *uuid = get_cached_uuid_t(0);
+ char *str;
+ uuid_rc_t rc;
+
+ rc = uuid_make(uuid, mode, ns, name);
+ if (rc != UUID_RC_OK)
+ pguuid_complain(rc);
+ str = uuid_to_string(uuid);
+
+ return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
+}
+
+
+static Datum
+uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
+{
+ uuid_t *ns_uuid = get_cached_uuid_t(1);
+
+ string_to_uuid(DatumGetCString(DirectFunctionCall1(uuid_out,
+ UUIDPGetDatum(ns))),
+ ns_uuid);
+
+ return uuid_generate_internal(mode,
+ ns_uuid,
+ text_to_cstring(name),
+ 0);
+}
+
+#else /* !HAVE_UUID_OSSP */
+
+static Datum
+uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
+{
+ char strbuf[40];
+
+ switch (v)
+ {
+ case 0: /* constant-value uuids */
+ strlcpy(strbuf, ptr, 37);
+ break;
+
+ case 1: /* time/node-based uuids */
+ {
+#ifdef HAVE_UUID_E2FS
+ uuid_t uu;
+
+ uuid_generate_time(uu);
+ uuid_unparse(uu, strbuf);
+
+ /*
+ * PTR, if set, replaces the trailing characters of the uuid;
+ * this is to support v1mc, where a random multicast MAC is
+ * used instead of the physical one
+ */
+ if (ptr && len <= 36)
+ strcpy(strbuf + (36 - len), ptr);
+#else /* BSD */
+ uuid_t uu;
+ uint32_t status = uuid_s_ok;
+ char *str = NULL;
+
+ uuid_create(&uu, &status);
+
+ if (status == uuid_s_ok)
+ {
+ uuid_to_string(&uu, &str, &status);
+ if (status == uuid_s_ok)
+ {
+ strlcpy(strbuf, str, 37);
+
+ /*
+ * In recent NetBSD, uuid_create() has started
+ * producing v4 instead of v1 UUIDs. Check the
+ * version field and complain if it's not v1.
+ */
+ if (strbuf[14] != '1')
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+ /* translator: %c will be a hex digit */
+ errmsg("uuid_create() produced a version %c UUID instead of the expected version 1",
+ strbuf[14])));
+
+ /*
+ * PTR, if set, replaces the trailing characters of
+ * the uuid; this is to support v1mc, where a random
+ * multicast MAC is used instead of the physical one
+ */
+ if (ptr && len <= 36)
+ strcpy(strbuf + (36 - len), ptr);
+ }
+ if (str)
+ free(str);
+ }
+
+ if (status != uuid_s_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+ errmsg("uuid library failure: %d",
+ (int) status)));
+#endif
+ break;
+ }
+
+ case 3: /* namespace-based MD5 uuids */
+ case 5: /* namespace-based SHA1 uuids */
+ {
+ dce_uuid_t uu;
+#ifdef HAVE_UUID_BSD
+ uint32_t status = uuid_s_ok;
+ char *str = NULL;
+#endif
+
+ if (v == 3)
+ {
+ pg_cryptohash_ctx *ctx = pg_cryptohash_create(PG_MD5);
+
+ if (pg_cryptohash_init(ctx) < 0)
+ elog(ERROR, "could not initialize %s context: %s", "MD5",
+ pg_cryptohash_error(ctx));
+ if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
+ pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
+ elog(ERROR, "could not update %s context: %s", "MD5",
+ pg_cryptohash_error(ctx));
+ /* we assume sizeof MD5 result is 16, same as UUID size */
+ if (pg_cryptohash_final(ctx, (unsigned char *) &uu,
+ sizeof(uu)) < 0)
+ elog(ERROR, "could not finalize %s context: %s", "MD5",
+ pg_cryptohash_error(ctx));
+ pg_cryptohash_free(ctx);
+ }
+ else
+ {
+ pg_cryptohash_ctx *ctx = pg_cryptohash_create(PG_SHA1);
+ unsigned char sha1result[SHA1_DIGEST_LENGTH];
+
+ if (pg_cryptohash_init(ctx) < 0)
+ elog(ERROR, "could not initialize %s context: %s", "SHA1",
+ pg_cryptohash_error(ctx));
+ if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
+ pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
+ elog(ERROR, "could not update %s context: %s", "SHA1",
+ pg_cryptohash_error(ctx));
+ if (pg_cryptohash_final(ctx, sha1result, sizeof(sha1result)) < 0)
+ elog(ERROR, "could not finalize %s context: %s", "SHA1",
+ pg_cryptohash_error(ctx));
+ pg_cryptohash_free(ctx);
+
+ memcpy(&uu, sha1result, sizeof(uu));
+ }
+
+ /* the calculated hash is using local order */
+ UUID_TO_NETWORK(uu);
+ UUID_V3_OR_V5(uu, v);
+
+#ifdef HAVE_UUID_E2FS
+ /* uuid_unparse expects local order */
+ UUID_TO_LOCAL(uu);
+ uuid_unparse((unsigned char *) &uu, strbuf);
+#else /* BSD */
+ uuid_to_string(&uu, &str, &status);
+
+ if (status == uuid_s_ok)
+ strlcpy(strbuf, str, 37);
+
+ if (str)
+ free(str);
+
+ if (status != uuid_s_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+ errmsg("uuid library failure: %d",
+ (int) status)));
+#endif
+ break;
+ }
+
+ case 4: /* random uuid */
+ default:
+ {
+#ifdef HAVE_UUID_E2FS
+ uuid_t uu;
+
+ uuid_generate_random(uu);
+ uuid_unparse(uu, strbuf);
+#else /* BSD */
+ snprintf(strbuf, sizeof(strbuf),
+ "%08lx-%04x-%04x-%04x-%04x%08lx",
+ (unsigned long) arc4random(),
+ (unsigned) (arc4random() & 0xffff),
+ (unsigned) ((arc4random() & 0xfff) | 0x4000),
+ (unsigned) ((arc4random() & 0x3fff) | 0x8000),
+ (unsigned) (arc4random() & 0xffff),
+ (unsigned long) arc4random());
+#endif
+ break;
+ }
+ }
+
+ return DirectFunctionCall1(uuid_in, CStringGetDatum(strbuf));
+}
+
+#endif /* HAVE_UUID_OSSP */
+
+
+Datum
+uuid_nil(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_UUID_OSSP
+ return special_uuid_value("nil");
+#else
+ return uuid_generate_internal(0, NULL,
+ "00000000-0000-0000-0000-000000000000", 36);
+#endif
+}
+
+
+Datum
+uuid_ns_dns(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_UUID_OSSP
+ return special_uuid_value("ns:DNS");
+#else
+ return uuid_generate_internal(0, NULL,
+ "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
+#endif
+}
+
+
+Datum
+uuid_ns_url(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_UUID_OSSP
+ return special_uuid_value("ns:URL");
+#else
+ return uuid_generate_internal(0, NULL,
+ "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
+#endif
+}
+
+
+Datum
+uuid_ns_oid(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_UUID_OSSP
+ return special_uuid_value("ns:OID");
+#else
+ return uuid_generate_internal(0, NULL,
+ "6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
+#endif
+}
+
+
+Datum
+uuid_ns_x500(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_UUID_OSSP
+ return special_uuid_value("ns:X500");
+#else
+ return uuid_generate_internal(0, NULL,
+ "6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
+#endif
+}
+
+
+Datum
+uuid_generate_v1(PG_FUNCTION_ARGS)
+{
+ return uuid_generate_internal(UUID_MAKE_V1, NULL, NULL, 0);
+}
+
+
+Datum
+uuid_generate_v1mc(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_UUID_OSSP
+ char *buf = NULL;
+#elif defined(HAVE_UUID_E2FS)
+ char strbuf[40];
+ char *buf;
+ uuid_t uu;
+
+ uuid_generate_random(uu);
+
+ /* set IEEE802 multicast and local-admin bits */
+ ((dce_uuid_t *) &uu)->node[0] |= 0x03;
+
+ uuid_unparse(uu, strbuf);
+ buf = strbuf + 24;
+#else /* BSD */
+ char buf[16];
+
+ /* set IEEE802 multicast and local-admin bits */
+ snprintf(buf, sizeof(buf), "-%04x%08lx",
+ (unsigned) ((arc4random() & 0xffff) | 0x0300),
+ (unsigned long) arc4random());
+#endif
+
+ return uuid_generate_internal(UUID_MAKE_V1 | UUID_MAKE_MC, NULL,
+ buf, 13);
+}
+
+
+Datum
+uuid_generate_v3(PG_FUNCTION_ARGS)
+{
+ pg_uuid_t *ns = PG_GETARG_UUID_P(0);
+ text *name = PG_GETARG_TEXT_PP(1);
+
+#ifdef HAVE_UUID_OSSP
+ return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name);
+#else
+ return uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) ns,
+ VARDATA_ANY(name), VARSIZE_ANY_EXHDR(name));
+#endif
+}
+
+
+Datum
+uuid_generate_v4(PG_FUNCTION_ARGS)
+{
+ return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL, 0);
+}
+
+
+Datum
+uuid_generate_v5(PG_FUNCTION_ARGS)
+{
+ pg_uuid_t *ns = PG_GETARG_UUID_P(0);
+ text *name = PG_GETARG_TEXT_PP(1);
+
+#ifdef HAVE_UUID_OSSP
+ return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name);
+#else
+ return uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) ns,
+ VARDATA_ANY(name), VARSIZE_ANY_EXHDR(name));
+#endif
+}