Home | History | Annotate | Line # | Download | only in liblutil
      1 /*	$NetBSD: uuid.c,v 1.4 2025/09/05 21:16:23 christos Exp $	*/
      2 
      3 /* uuid.c -- Universally Unique Identifier routines */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2000-2024 The OpenLDAP Foundation.
      8  * Portions Copyright 2000-2003 Kurt D. Zeilenga.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted only as authorized by the OpenLDAP
     13  * Public License.
     14  *
     15  * A copy of this license is available in the file LICENSE in the
     16  * top-level directory of the distribution or, alternatively, at
     17  * <http://www.OpenLDAP.org/license.html>.
     18  */
     19 /* Portions Copyright 2000, John E. Schimmel, All rights reserved.
     20  * This software is not subject to any license of Mirapoint, Inc.
     21  *
     22  * This is free software; you can redistribute and use it
     23  * under the same terms as OpenLDAP itself.
     24  */
     25 /* This work was initially developed by John E. Schimmel and adapted
     26  * for inclusion in OpenLDAP Software by Kurt D. Zeilenga.
     27  */
     28 
     29 /*
     30  * Sorry this file is so scary, but it needs to run on a wide range of
     31  * platforms.  The only exported routine is lutil_uuidstr() which is all
     32  * that LDAP cares about.  It generates a new uuid and returns it in
     33  * in string form.
     34  */
     35 #include <sys/cdefs.h>
     36 __RCSID("$NetBSD: uuid.c,v 1.4 2025/09/05 21:16:23 christos Exp $");
     37 
     38 #include "portable.h"
     39 
     40 #include <limits.h>
     41 #include <stdio.h>
     42 #include <sys/types.h>
     43 
     44 #include <ac/stdlib.h>
     45 #include <ac/string.h>	/* get memcmp() */
     46 
     47 #ifdef HAVE_UUID_TO_STR
     48 #  include <sys/uuid.h>
     49 #elif defined( HAVE_UUID_GENERATE )
     50 #  include <uuid/uuid.h>
     51 #elif defined( _WIN32 )
     52 #  include <rpc.h>
     53 #else
     54 #  include <ac/socket.h>
     55 #  include <ac/time.h>
     56 #  ifdef HAVE_SYS_SYSCTL_H
     57 #    include <net/if.h>
     58 #    include <sys/sysctl.h>
     59 #    include <net/route.h>
     60 #  endif
     61 #endif
     62 
     63 #include <lutil.h>
     64 
     65 /* not needed for Windows */
     66 #if !defined(HAVE_UUID_TO_STR) && !defined(HAVE_UUID_GENERATE) && !defined(_WIN32)
     67 static unsigned char *
     68 lutil_eaddr( void )
     69 {
     70 	static unsigned char zero[6];
     71 	static unsigned char eaddr[6];
     72 
     73 #ifdef HAVE_SYS_SYSCTL_H
     74 	size_t needed;
     75 	int mib[6];
     76 	char *buf, *next, *lim;
     77 	struct if_msghdr *ifm;
     78 	struct sockaddr_dl *sdl;
     79 
     80 	if (memcmp(eaddr, zero, sizeof(eaddr))) {
     81 		return eaddr;
     82 	}
     83 
     84 	mib[0] = CTL_NET;
     85 	mib[1] = PF_ROUTE;
     86 	mib[3] = 0;
     87 	mib[3] = 0;
     88 	mib[4] = NET_RT_IFLIST;
     89 	mib[5] = 0;
     90 
     91 	if (sysctl(mib, sizeof(mib), NULL, &needed, NULL, 0) < 0) {
     92 		return NULL;
     93 	}
     94 
     95 	buf = malloc(needed);
     96 	if( buf == NULL ) return NULL;
     97 
     98 	if (sysctl(mib, sizeof(mib), buf, &needed, NULL, 0) < 0) {
     99 		free(buf);
    100 		return NULL;
    101 	}
    102 
    103 	lim = buf + needed;
    104 	for (next = buf; next < lim; next += ifm->ifm_msglen) {
    105 		ifm = (struct if_msghdr *)next;
    106 		sdl = (struct sockaddr_dl *)(ifm + 1);
    107 
    108 		if ( sdl->sdl_family != AF_LINK || sdl->sdl_alen == 6 ) {
    109 			AC_MEMCPY(eaddr,
    110 				(unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
    111 				sizeof(eaddr));
    112 			free(buf);
    113 			return eaddr;
    114 		}
    115 	}
    116 
    117 	free(buf);
    118 	return NULL;
    119 
    120 #elif defined( SIOCGIFADDR ) && defined( AFLINK )
    121 	char buf[sizeof(struct ifreq) * 32];
    122 	struct ifconf ifc;
    123 	struct ifreq *ifr;
    124 	struct sockaddr *sa;
    125 	struct sockaddr_dl *sdl;
    126 	unsigned char *p;
    127 	int s, i;
    128 
    129 	if (memcmp(eaddr, zero, sizeof(eaddr))) {
    130 		return eaddr;
    131 	}
    132 
    133 	s = socket( AF_INET, SOCK_DGRAM, 0 );
    134 	if ( s < 0 ) {
    135 		return NULL;
    136 	}
    137 
    138 	ifc.ifc_len = sizeof( buf );
    139 	ifc.ifc_buf = buf;
    140 	memset( buf, 0, sizeof( buf ) );
    141 
    142 	i = ioctl( s, SIOCGIFCONF, (char *)&ifc );
    143 	close( s );
    144 
    145 	if( i < 0 ) {
    146 		return NULL;
    147 	}
    148 
    149 	for ( i = 0; i < ifc.ifc_len; ) {
    150 		ifr = (struct ifreq *)&ifc.ifc_buf[i];
    151 		sa = &ifr->ifr_addr;
    152 
    153 		if ( sa->sa_len > sizeof( ifr->ifr_addr ) ) {
    154 			i += sizeof( ifr->ifr_name ) + sa->sa_len;
    155 		} else {
    156 			i += sizeof( *ifr );
    157 		}
    158 
    159 		if ( sa->sa_family != AF_LINK ) {
    160 			continue;
    161 		}
    162 
    163 		sdl = (struct sockaddr_dl *)sa;
    164 
    165 		if ( sdl->sdl_alen == 6 ) {
    166 			AC_MEMCPY(eaddr,
    167 				(unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
    168 				sizeof(eaddr));
    169 			return eaddr;
    170 		}
    171 	}
    172 
    173 	return NULL;
    174 
    175 #else
    176 	if (memcmp(eaddr, zero, sizeof(eaddr)) == 0) {
    177 		/* XXX - who knows? */
    178 		lutil_entropy( eaddr, sizeof(eaddr) );
    179 		eaddr[0] |= 0x01; /* turn it into a multicast address */
    180 	}
    181 
    182 	return eaddr;
    183 #endif
    184 }
    185 
    186 #if (ULONG_MAX >> 31 >> 31) > 1 || defined HAVE_LONG_LONG
    187 
    188 #if (ULONG_MAX >> 31 >> 31) > 1
    189     typedef unsigned long       UI64;
    190 	/* 100 usec intervals from 10/10/1582 to 1/1/1970 */
    191 #   define UUID_TPLUS           0x01B21DD2138140ul
    192 #else
    193     typedef unsigned long long  UI64;
    194 #   define UUID_TPLUS           0x01B21DD2138140ull
    195 #endif
    196 
    197 #define high32(i)           ((unsigned long) ((i) >> 32))
    198 #define low32(i)            ((unsigned long) (i) & 0xFFFFFFFFul)
    199 #define set_add64(res, i)   ((res) += (i))
    200 #define set_add64l(res, i)  ((res) += (i))
    201 #define mul64ll(i1, i2)     ((UI64) (i1) * (i2))
    202 
    203 #else /* ! (ULONG_MAX >= 64 bits || HAVE_LONG_LONG) */
    204 
    205 typedef struct {
    206 	unsigned long high, low;
    207 } UI64;
    208 
    209 static const UI64 UUID_TPLUS = { 0x01B21Dul, 0xD2138140ul };
    210 
    211 #define high32(i)			 ((i).high)
    212 #define low32(i)			 ((i).low)
    213 
    214 /* res += ui64 */
    215 #define set_add64(res, ui64) \
    216 { \
    217 	res.high += ui64.high; \
    218 	res.low	 = (res.low + ui64.low) & 0xFFFFFFFFul; \
    219 	if (res.low < ui64.low) res.high++; \
    220 }
    221 
    222 /* res += ul32 */
    223 #define set_add64l(res, ul32) \
    224 { \
    225 	res.low	= (res.low + ul32) & 0xFFFFFFFFul; \
    226 	if (res.low < ul32) res.high++; \
    227 }
    228 
    229 /* compute i1 * i2 */
    230 static UI64
    231 mul64ll(unsigned long i1, unsigned long i2)
    232 {
    233 	const unsigned int high1 = (i1 >> 16), low1 = (i1 & 0xffff);
    234 	const unsigned int high2 = (i2 >> 16), low2 = (i2 & 0xffff);
    235 
    236 	UI64 res;
    237 	unsigned long tmp;
    238 
    239 	res.high = (unsigned long) high1 * high2;
    240 	res.low	 = (unsigned long) low1	 * low2;
    241 
    242 	tmp = (unsigned long) low1 * high2;
    243 	res.high += (tmp >> 16);
    244 	tmp = (tmp << 16) & 0xFFFFFFFFul;
    245 	res.low = (res.low + tmp) & 0xFFFFFFFFul;
    246 	if (res.low < tmp)
    247 		res.high++;
    248 
    249 	tmp = (unsigned long) low2 * high1;
    250 	res.high += (tmp >> 16);
    251 	tmp = (tmp << 16) & 0xFFFFFFFFul;
    252 	res.low = (res.low + tmp) & 0xFFFFFFFFul;
    253 	if (res.low < tmp)
    254 		res.high++;
    255 
    256 	return res;
    257 }
    258 
    259 #endif /* ULONG_MAX >= 64 bits || HAVE_LONG_LONG */
    260 
    261 #endif /* !HAVE_UUID_TO_STR && !HAVE_UUID_GENERATE && !_WIN32 */
    262 
    263 /*
    264 ** All we really care about is an ISO UUID string.  The format of a UUID is:
    265 **	field			octet		note
    266 **	time_low		0-3		low field of the timestamp
    267 **	time_mid		4-5		middle field of timestamp
    268 **	time_hi_and_version	6-7		high field of timestamp and
    269 **						version number
    270 **	clock_seq_hi_and_resv	8		high field of clock sequence
    271 **						and variant
    272 **	clock_seq_low		9		low field of clock sequence
    273 **	node			10-15		spacially unique identifier
    274 **
    275 ** We use DCE version one, and the DCE variant.  Our unique identifier is
    276 ** the first ethernet address on the system.
    277 */
    278 size_t
    279 lutil_uuidstr( char *buf, size_t len )
    280 {
    281 #ifdef HAVE_UUID_TO_STR
    282 	uuid_t uu = {0};
    283 	unsigned rc;
    284 	char *s;
    285 	size_t l;
    286 
    287 	uuid_create( &uu, &rc );
    288 	if ( rc != uuid_s_ok ) {
    289 		return 0;
    290 	}
    291 
    292 	uuid_to_str( &uu, &s, &rc );
    293 	if ( rc != uuid_s_ok ) {
    294 		return 0;
    295 	}
    296 
    297 	l = strlen( s );
    298 	if ( l >= len ) {
    299 		free( s );
    300 		return 0;
    301 	}
    302 
    303 	strncpy( buf, s, len );
    304 	free( s );
    305 
    306 	return l;
    307 
    308 #elif defined( HAVE_UUID_GENERATE )
    309 	uuid_t uu;
    310 
    311 	uuid_generate( uu );
    312 	uuid_unparse_lower( uu, buf );
    313 	return strlen( buf );
    314 
    315 #elif defined( _WIN32 )
    316 	UUID uuid;
    317 	unsigned char *uuidstr;
    318 	size_t uuidlen;
    319 
    320 	if( UuidCreate( &uuid ) != RPC_S_OK ) {
    321 		return 0;
    322 	}
    323 
    324 	if( UuidToString( &uuid, &uuidstr ) !=  RPC_S_OK ) {
    325 		return 0;
    326 	}
    327 
    328 	uuidlen = strlen( uuidstr );
    329 	if( uuidlen >= len ) {
    330 		return 0;
    331 	}
    332 
    333 	strncpy( buf, uuidstr, len );
    334 	RpcStringFree( &uuidstr );
    335 
    336 	return uuidlen;
    337 
    338 #else
    339 	struct timeval tv;
    340 	UI64 tl;
    341 	unsigned char *nl;
    342 	unsigned short t2, t3, s1;
    343 	unsigned long t1, tl_high;
    344 	unsigned int rc;
    345 
    346 	/*
    347 	 * Theoretically we should delay if seq wraps within 100usec but for now
    348 	 * systems are not fast enough to worry about it.
    349 	 */
    350 	static int inited = 0;
    351 	static unsigned short seq;
    352 
    353 	if (!inited) {
    354 		lutil_entropy( (unsigned char *) &seq, sizeof(seq) );
    355 		inited++;
    356 	}
    357 
    358 #ifdef HAVE_GETTIMEOFDAY
    359 	gettimeofday( &tv, 0 );
    360 #else
    361 	time( &tv.tv_sec );
    362 	tv.tv_usec = 0;
    363 #endif
    364 
    365 	tl = mul64ll(tv.tv_sec, 10000000UL);
    366 	set_add64l(tl, tv.tv_usec * 10UL);
    367 	set_add64(tl, UUID_TPLUS);
    368 
    369 	nl = lutil_eaddr();
    370 
    371 	t1 = low32(tl);				/* time_low */
    372 	tl_high = high32(tl);
    373 	t2 = tl_high & 0xffff;		/* time_mid */
    374 	t3 = ((tl_high >> 16) & 0x0fff) | 0x1000;	/* time_hi_and_version */
    375 	s1 = ( ++seq & 0x1fff ) | 0x8000;		/* clock_seq_and_reserved */
    376 
    377 	rc = snprintf( buf, len,
    378 		"%08lx-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
    379 		t1, (unsigned) t2, (unsigned) t3, (unsigned) s1,
    380 		(unsigned) nl[0], (unsigned) nl[1],
    381 		(unsigned) nl[2], (unsigned) nl[3],
    382 		(unsigned) nl[4], (unsigned) nl[5] );
    383 
    384 	return rc < len ? rc : 0;
    385 #endif
    386 }
    387 
    388 int
    389 lutil_uuidstr_from_normalized(
    390 	char		*uuid,
    391 	size_t		uuidlen,
    392 	char		*buf,
    393 	size_t		buflen )
    394 {
    395 	unsigned char nibble;
    396 	int i, d = 0;
    397 
    398 	assert( uuid != NULL );
    399 	assert( buf != NULL );
    400 
    401 	if ( uuidlen != 16 ) return -1;
    402 	if ( buflen < 36 ) return -1;
    403 
    404 	for ( i = 0; i < 16; i++ ) {
    405 		if ( i == 4 || i == 6 || i == 8 || i == 10 ) {
    406 			buf[(i<<1)+d] = '-';
    407 			d += 1;
    408 		}
    409 
    410 		nibble = (uuid[i] >> 4) & 0xF;
    411 		if ( nibble < 10 ) {
    412 			buf[(i<<1)+d] = nibble + '0';
    413 		} else {
    414 			buf[(i<<1)+d] = nibble - 10 + 'a';
    415 		}
    416 
    417 		nibble = (uuid[i]) & 0xF;
    418 		if ( nibble < 10 ) {
    419 			buf[(i<<1)+d+1] = nibble + '0';
    420 		} else {
    421 			buf[(i<<1)+d+1] = nibble - 10 + 'a';
    422 		}
    423 	}
    424 
    425 	if ( buflen > 36 ) buf[36] = '\0';
    426 	return 36;
    427 }
    428 
    429 #ifdef TEST
    430 int
    431 main(int argc, char **argv)
    432 {
    433 	char buf1[8], buf2[64];
    434 
    435 #ifndef HAVE_UUID_TO_STR
    436 	unsigned char *p = lutil_eaddr();
    437 
    438 	if( p ) {
    439 		printf( "Ethernet Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
    440 			(unsigned) p[0], (unsigned) p[1], (unsigned) p[2],
    441 			(unsigned) p[3], (unsigned) p[4], (unsigned) p[5]);
    442 	}
    443 #endif
    444 
    445 	if ( lutil_uuidstr( buf1, sizeof( buf1 ) ) ) {
    446 		printf( "UUID: %s\n", buf1 );
    447 	} else {
    448 		fprintf( stderr, "too short: %ld\n", (long) sizeof( buf1 ) );
    449 	}
    450 
    451 	if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
    452 		printf( "UUID: %s\n", buf2 );
    453 	} else {
    454 		fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
    455 	}
    456 
    457 	if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
    458 		printf( "UUID: %s\n", buf2 );
    459 	} else {
    460 		fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
    461 	}
    462 
    463 	return 0;
    464 }
    465 #endif
    466