summaryrefslogtreecommitdiffstats
path: root/libraries/liblutil/uuid.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/liblutil/uuid.c')
-rw-r--r--libraries/liblutil/uuid.c460
1 files changed, 460 insertions, 0 deletions
diff --git a/libraries/liblutil/uuid.c b/libraries/liblutil/uuid.c
new file mode 100644
index 0000000..061b2a2
--- /dev/null
+++ b/libraries/liblutil/uuid.c
@@ -0,0 +1,460 @@
+/* uuid.c -- Universally Unique Identifier routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright 2000, John E. Schimmel, All rights reserved.
+ * This software is not subject to any license of Mirapoint, Inc.
+ *
+ * This is free software; you can redistribute and use it
+ * under the same terms as OpenLDAP itself.
+ */
+/* This work was initially developed by John E. Schimmel and adapted
+ * for inclusion in OpenLDAP Software by Kurt D. Zeilenga.
+ */
+
+/*
+ * Sorry this file is so scary, but it needs to run on a wide range of
+ * platforms. The only exported routine is lutil_uuidstr() which is all
+ * that LDAP cares about. It generates a new uuid and returns it in
+ * in string form.
+ */
+#include "portable.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h> /* get memcmp() */
+
+#ifdef HAVE_UUID_TO_STR
+# include <sys/uuid.h>
+#elif defined( HAVE_UUID_GENERATE )
+# include <uuid/uuid.h>
+#elif defined( _WIN32 )
+# include <rpc.h>
+#else
+# include <ac/socket.h>
+# include <ac/time.h>
+# ifdef HAVE_SYS_SYSCTL_H
+# include <net/if.h>
+# include <sys/sysctl.h>
+# include <net/route.h>
+# endif
+#endif
+
+#include <lutil.h>
+
+/* not needed for Windows */
+#if !defined(HAVE_UUID_TO_STR) && !defined(HAVE_UUID_GENERATE) && !defined(_WIN32)
+static unsigned char *
+lutil_eaddr( void )
+{
+ static unsigned char zero[6];
+ static unsigned char eaddr[6];
+
+#ifdef HAVE_SYS_SYSCTL_H
+ size_t needed;
+ int mib[6];
+ char *buf, *next, *lim;
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+
+ if (memcmp(eaddr, zero, sizeof(eaddr))) {
+ return eaddr;
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[3] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, sizeof(mib), NULL, &needed, NULL, 0) < 0) {
+ return NULL;
+ }
+
+ buf = malloc(needed);
+ if( buf == NULL ) return NULL;
+
+ if (sysctl(mib, sizeof(mib), buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return NULL;
+ }
+
+ lim = buf + needed;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+
+ if ( sdl->sdl_family != AF_LINK || sdl->sdl_alen == 6 ) {
+ AC_MEMCPY(eaddr,
+ (unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
+ sizeof(eaddr));
+ free(buf);
+ return eaddr;
+ }
+ }
+
+ free(buf);
+ return NULL;
+
+#elif defined( SIOCGIFADDR ) && defined( AFLINK )
+ char buf[sizeof(struct ifreq) * 32];
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ struct sockaddr *sa;
+ struct sockaddr_dl *sdl;
+ unsigned char *p;
+ int s, i;
+
+ if (memcmp(eaddr, zero, sizeof(eaddr))) {
+ return eaddr;
+ }
+
+ s = socket( AF_INET, SOCK_DGRAM, 0 );
+ if ( s < 0 ) {
+ return NULL;
+ }
+
+ ifc.ifc_len = sizeof( buf );
+ ifc.ifc_buf = buf;
+ memset( buf, 0, sizeof( buf ) );
+
+ i = ioctl( s, SIOCGIFCONF, (char *)&ifc );
+ close( s );
+
+ if( i < 0 ) {
+ return NULL;
+ }
+
+ for ( i = 0; i < ifc.ifc_len; ) {
+ ifr = (struct ifreq *)&ifc.ifc_buf[i];
+ sa = &ifr->ifr_addr;
+
+ if ( sa->sa_len > sizeof( ifr->ifr_addr ) ) {
+ i += sizeof( ifr->ifr_name ) + sa->sa_len;
+ } else {
+ i += sizeof( *ifr );
+ }
+
+ if ( sa->sa_family != AF_LINK ) {
+ continue;
+ }
+
+ sdl = (struct sockaddr_dl *)sa;
+
+ if ( sdl->sdl_alen == 6 ) {
+ AC_MEMCPY(eaddr,
+ (unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
+ sizeof(eaddr));
+ return eaddr;
+ }
+ }
+
+ return NULL;
+
+#else
+ if (memcmp(eaddr, zero, sizeof(eaddr)) == 0) {
+ /* XXX - who knows? */
+ lutil_entropy( eaddr, sizeof(eaddr) );
+ eaddr[0] |= 0x01; /* turn it into a multicast address */
+ }
+
+ return eaddr;
+#endif
+}
+
+#if (ULONG_MAX >> 31 >> 31) > 1 || defined HAVE_LONG_LONG
+
+#if (ULONG_MAX >> 31 >> 31) > 1
+ typedef unsigned long UI64;
+ /* 100 usec intervals from 10/10/1582 to 1/1/1970 */
+# define UUID_TPLUS 0x01B21DD2138140ul
+#else
+ typedef unsigned long long UI64;
+# define UUID_TPLUS 0x01B21DD2138140ull
+#endif
+
+#define high32(i) ((unsigned long) ((i) >> 32))
+#define low32(i) ((unsigned long) (i) & 0xFFFFFFFFul)
+#define set_add64(res, i) ((res) += (i))
+#define set_add64l(res, i) ((res) += (i))
+#define mul64ll(i1, i2) ((UI64) (i1) * (i2))
+
+#else /* ! (ULONG_MAX >= 64 bits || HAVE_LONG_LONG) */
+
+typedef struct {
+ unsigned long high, low;
+} UI64;
+
+static const UI64 UUID_TPLUS = { 0x01B21Dul, 0xD2138140ul };
+
+#define high32(i) ((i).high)
+#define low32(i) ((i).low)
+
+/* res += ui64 */
+#define set_add64(res, ui64) \
+{ \
+ res.high += ui64.high; \
+ res.low = (res.low + ui64.low) & 0xFFFFFFFFul; \
+ if (res.low < ui64.low) res.high++; \
+}
+
+/* res += ul32 */
+#define set_add64l(res, ul32) \
+{ \
+ res.low = (res.low + ul32) & 0xFFFFFFFFul; \
+ if (res.low < ul32) res.high++; \
+}
+
+/* compute i1 * i2 */
+static UI64
+mul64ll(unsigned long i1, unsigned long i2)
+{
+ const unsigned int high1 = (i1 >> 16), low1 = (i1 & 0xffff);
+ const unsigned int high2 = (i2 >> 16), low2 = (i2 & 0xffff);
+
+ UI64 res;
+ unsigned long tmp;
+
+ res.high = (unsigned long) high1 * high2;
+ res.low = (unsigned long) low1 * low2;
+
+ tmp = (unsigned long) low1 * high2;
+ res.high += (tmp >> 16);
+ tmp = (tmp << 16) & 0xFFFFFFFFul;
+ res.low = (res.low + tmp) & 0xFFFFFFFFul;
+ if (res.low < tmp)
+ res.high++;
+
+ tmp = (unsigned long) low2 * high1;
+ res.high += (tmp >> 16);
+ tmp = (tmp << 16) & 0xFFFFFFFFul;
+ res.low = (res.low + tmp) & 0xFFFFFFFFul;
+ if (res.low < tmp)
+ res.high++;
+
+ return res;
+}
+
+#endif /* ULONG_MAX >= 64 bits || HAVE_LONG_LONG */
+
+#endif /* !HAVE_UUID_TO_STR && !HAVE_UUID_GENERATE && !_WIN32 */
+
+/*
+** All we really care about is an ISO UUID string. The format of a UUID is:
+** field octet note
+** time_low 0-3 low field of the timestamp
+** time_mid 4-5 middle field of timestamp
+** time_hi_and_version 6-7 high field of timestamp and
+** version number
+** clock_seq_hi_and_resv 8 high field of clock sequence
+** and variant
+** clock_seq_low 9 low field of clock sequence
+** node 10-15 spacially unique identifier
+**
+** We use DCE version one, and the DCE variant. Our unique identifier is
+** the first ethernet address on the system.
+*/
+size_t
+lutil_uuidstr( char *buf, size_t len )
+{
+#ifdef HAVE_UUID_TO_STR
+ uuid_t uu = {0};
+ unsigned rc;
+ char *s;
+ size_t l;
+
+ uuid_create( &uu, &rc );
+ if ( rc != uuid_s_ok ) {
+ return 0;
+ }
+
+ uuid_to_str( &uu, &s, &rc );
+ if ( rc != uuid_s_ok ) {
+ return 0;
+ }
+
+ l = strlen( s );
+ if ( l >= len ) {
+ free( s );
+ return 0;
+ }
+
+ strncpy( buf, s, len );
+ free( s );
+
+ return l;
+
+#elif defined( HAVE_UUID_GENERATE )
+ uuid_t uu;
+
+ uuid_generate( uu );
+ uuid_unparse_lower( uu, buf );
+ return strlen( buf );
+
+#elif defined( _WIN32 )
+ UUID uuid;
+ unsigned char *uuidstr;
+ size_t uuidlen;
+
+ if( UuidCreate( &uuid ) != RPC_S_OK ) {
+ return 0;
+ }
+
+ if( UuidToString( &uuid, &uuidstr ) != RPC_S_OK ) {
+ return 0;
+ }
+
+ uuidlen = strlen( uuidstr );
+ if( uuidlen >= len ) {
+ return 0;
+ }
+
+ strncpy( buf, uuidstr, len );
+ RpcStringFree( &uuidstr );
+
+ return uuidlen;
+
+#else
+ struct timeval tv;
+ UI64 tl;
+ unsigned char *nl;
+ unsigned short t2, t3, s1;
+ unsigned long t1, tl_high;
+ unsigned int rc;
+
+ /*
+ * Theoretically we should delay if seq wraps within 100usec but for now
+ * systems are not fast enough to worry about it.
+ */
+ static int inited = 0;
+ static unsigned short seq;
+
+ if (!inited) {
+ lutil_entropy( (unsigned char *) &seq, sizeof(seq) );
+ inited++;
+ }
+
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &tv, 0 );
+#else
+ time( &tv.tv_sec );
+ tv.tv_usec = 0;
+#endif
+
+ tl = mul64ll(tv.tv_sec, 10000000UL);
+ set_add64l(tl, tv.tv_usec * 10UL);
+ set_add64(tl, UUID_TPLUS);
+
+ nl = lutil_eaddr();
+
+ t1 = low32(tl); /* time_low */
+ tl_high = high32(tl);
+ t2 = tl_high & 0xffff; /* time_mid */
+ t3 = ((tl_high >> 16) & 0x0fff) | 0x1000; /* time_hi_and_version */
+ s1 = ( ++seq & 0x1fff ) | 0x8000; /* clock_seq_and_reserved */
+
+ rc = snprintf( buf, len,
+ "%08lx-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
+ t1, (unsigned) t2, (unsigned) t3, (unsigned) s1,
+ (unsigned) nl[0], (unsigned) nl[1],
+ (unsigned) nl[2], (unsigned) nl[3],
+ (unsigned) nl[4], (unsigned) nl[5] );
+
+ return rc < len ? rc : 0;
+#endif
+}
+
+int
+lutil_uuidstr_from_normalized(
+ char *uuid,
+ size_t uuidlen,
+ char *buf,
+ size_t buflen )
+{
+ unsigned char nibble;
+ int i, d = 0;
+
+ assert( uuid != NULL );
+ assert( buf != NULL );
+
+ if ( uuidlen != 16 ) return -1;
+ if ( buflen < 36 ) return -1;
+
+ for ( i = 0; i < 16; i++ ) {
+ if ( i == 4 || i == 6 || i == 8 || i == 10 ) {
+ buf[(i<<1)+d] = '-';
+ d += 1;
+ }
+
+ nibble = (uuid[i] >> 4) & 0xF;
+ if ( nibble < 10 ) {
+ buf[(i<<1)+d] = nibble + '0';
+ } else {
+ buf[(i<<1)+d] = nibble - 10 + 'a';
+ }
+
+ nibble = (uuid[i]) & 0xF;
+ if ( nibble < 10 ) {
+ buf[(i<<1)+d+1] = nibble + '0';
+ } else {
+ buf[(i<<1)+d+1] = nibble - 10 + 'a';
+ }
+ }
+
+ if ( buflen > 36 ) buf[36] = '\0';
+ return 36;
+}
+
+#ifdef TEST
+int
+main(int argc, char **argv)
+{
+ char buf1[8], buf2[64];
+
+#ifndef HAVE_UUID_TO_STR
+ unsigned char *p = lutil_eaddr();
+
+ if( p ) {
+ printf( "Ethernet Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ (unsigned) p[0], (unsigned) p[1], (unsigned) p[2],
+ (unsigned) p[3], (unsigned) p[4], (unsigned) p[5]);
+ }
+#endif
+
+ if ( lutil_uuidstr( buf1, sizeof( buf1 ) ) ) {
+ printf( "UUID: %s\n", buf1 );
+ } else {
+ fprintf( stderr, "too short: %ld\n", (long) sizeof( buf1 ) );
+ }
+
+ if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
+ printf( "UUID: %s\n", buf2 );
+ } else {
+ fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
+ }
+
+ if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
+ printf( "UUID: %s\n", buf2 );
+ } else {
+ fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
+ }
+
+ return 0;
+}
+#endif