/* uuid.c -- Universally Unique Identifier routines */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * 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 * . */ /* 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 #include #include #include #include /* get memcmp() */ #ifdef HAVE_UUID_TO_STR # include #elif defined( HAVE_UUID_GENERATE ) # include #elif defined( _WIN32 ) # include #else # include # include # ifdef HAVE_SYS_SYSCTL_H # include # include # include # endif #endif #include /* 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