diff options
Diffstat (limited to 'libraries/liblutil/uuid.c')
-rw-r--r-- | libraries/liblutil/uuid.c | 460 |
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..62a4e7a --- /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-2018 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 |