1*549b59edSchristos /* $NetBSD: uuid.c,v 1.3 2021/08/14 16:14:58 christos Exp $ */
24e6df137Slukem
32de962bdSlukem /* uuid.c -- Universally Unique Identifier routines */
4d11b170bStron /* $OpenLDAP$ */
52de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
62de962bdSlukem *
7*549b59edSchristos * Copyright 2000-2021 The OpenLDAP Foundation.
82de962bdSlukem * Portions Copyright 2000-2003 Kurt D. Zeilenga.
92de962bdSlukem * All rights reserved.
102de962bdSlukem *
112de962bdSlukem * Redistribution and use in source and binary forms, with or without
122de962bdSlukem * modification, are permitted only as authorized by the OpenLDAP
132de962bdSlukem * Public License.
142de962bdSlukem *
152de962bdSlukem * A copy of this license is available in the file LICENSE in the
162de962bdSlukem * top-level directory of the distribution or, alternatively, at
172de962bdSlukem * <http://www.OpenLDAP.org/license.html>.
182de962bdSlukem */
192de962bdSlukem /* Portions Copyright 2000, John E. Schimmel, All rights reserved.
202de962bdSlukem * This software is not subject to any license of Mirapoint, Inc.
212de962bdSlukem *
222de962bdSlukem * This is free software; you can redistribute and use it
232de962bdSlukem * under the same terms as OpenLDAP itself.
242de962bdSlukem */
252de962bdSlukem /* This work was initially developed by John E. Schimmel and adapted
262de962bdSlukem * for inclusion in OpenLDAP Software by Kurt D. Zeilenga.
272de962bdSlukem */
282de962bdSlukem
292de962bdSlukem /*
302de962bdSlukem * Sorry this file is so scary, but it needs to run on a wide range of
312de962bdSlukem * platforms. The only exported routine is lutil_uuidstr() which is all
322de962bdSlukem * that LDAP cares about. It generates a new uuid and returns it in
332de962bdSlukem * in string form.
342de962bdSlukem */
35376af7d7Schristos #include <sys/cdefs.h>
36*549b59edSchristos __RCSID("$NetBSD: uuid.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
37376af7d7Schristos
382de962bdSlukem #include "portable.h"
392de962bdSlukem
402de962bdSlukem #include <limits.h>
412de962bdSlukem #include <stdio.h>
422de962bdSlukem #include <sys/types.h>
432de962bdSlukem
442de962bdSlukem #include <ac/stdlib.h>
452de962bdSlukem #include <ac/string.h> /* get memcmp() */
462de962bdSlukem
472de962bdSlukem #ifdef HAVE_UUID_TO_STR
482de962bdSlukem # include <sys/uuid.h>
492de962bdSlukem #elif defined( HAVE_UUID_GENERATE )
502de962bdSlukem # include <uuid/uuid.h>
512de962bdSlukem #elif defined( _WIN32 )
522de962bdSlukem # include <rpc.h>
532de962bdSlukem #else
542de962bdSlukem # include <ac/socket.h>
552de962bdSlukem # include <ac/time.h>
562de962bdSlukem # ifdef HAVE_SYS_SYSCTL_H
572de962bdSlukem # include <net/if.h>
582de962bdSlukem # include <sys/sysctl.h>
592de962bdSlukem # include <net/route.h>
602de962bdSlukem # endif
612de962bdSlukem #endif
622de962bdSlukem
632de962bdSlukem #include <lutil.h>
642de962bdSlukem
652de962bdSlukem /* not needed for Windows */
662de962bdSlukem #if !defined(HAVE_UUID_TO_STR) && !defined(HAVE_UUID_GENERATE) && !defined(_WIN32)
672de962bdSlukem static unsigned char *
lutil_eaddr(void)682de962bdSlukem lutil_eaddr( void )
692de962bdSlukem {
702de962bdSlukem static unsigned char zero[6];
712de962bdSlukem static unsigned char eaddr[6];
722de962bdSlukem
732de962bdSlukem #ifdef HAVE_SYS_SYSCTL_H
742de962bdSlukem size_t needed;
752de962bdSlukem int mib[6];
762de962bdSlukem char *buf, *next, *lim;
772de962bdSlukem struct if_msghdr *ifm;
782de962bdSlukem struct sockaddr_dl *sdl;
792de962bdSlukem
802de962bdSlukem if (memcmp(eaddr, zero, sizeof(eaddr))) {
812de962bdSlukem return eaddr;
822de962bdSlukem }
832de962bdSlukem
842de962bdSlukem mib[0] = CTL_NET;
852de962bdSlukem mib[1] = PF_ROUTE;
862de962bdSlukem mib[3] = 0;
872de962bdSlukem mib[3] = 0;
882de962bdSlukem mib[4] = NET_RT_IFLIST;
892de962bdSlukem mib[5] = 0;
902de962bdSlukem
912de962bdSlukem if (sysctl(mib, sizeof(mib), NULL, &needed, NULL, 0) < 0) {
922de962bdSlukem return NULL;
932de962bdSlukem }
942de962bdSlukem
952de962bdSlukem buf = malloc(needed);
962de962bdSlukem if( buf == NULL ) return NULL;
972de962bdSlukem
982de962bdSlukem if (sysctl(mib, sizeof(mib), buf, &needed, NULL, 0) < 0) {
992de962bdSlukem free(buf);
1002de962bdSlukem return NULL;
1012de962bdSlukem }
1022de962bdSlukem
1032de962bdSlukem lim = buf + needed;
1042de962bdSlukem for (next = buf; next < lim; next += ifm->ifm_msglen) {
1052de962bdSlukem ifm = (struct if_msghdr *)next;
1062de962bdSlukem sdl = (struct sockaddr_dl *)(ifm + 1);
1072de962bdSlukem
1082de962bdSlukem if ( sdl->sdl_family != AF_LINK || sdl->sdl_alen == 6 ) {
1092de962bdSlukem AC_MEMCPY(eaddr,
1102de962bdSlukem (unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
1112de962bdSlukem sizeof(eaddr));
1122de962bdSlukem free(buf);
1132de962bdSlukem return eaddr;
1142de962bdSlukem }
1152de962bdSlukem }
1162de962bdSlukem
1172de962bdSlukem free(buf);
1182de962bdSlukem return NULL;
1192de962bdSlukem
1202de962bdSlukem #elif defined( SIOCGIFADDR ) && defined( AFLINK )
1212de962bdSlukem char buf[sizeof(struct ifreq) * 32];
1222de962bdSlukem struct ifconf ifc;
1232de962bdSlukem struct ifreq *ifr;
1242de962bdSlukem struct sockaddr *sa;
1252de962bdSlukem struct sockaddr_dl *sdl;
1262de962bdSlukem unsigned char *p;
1272de962bdSlukem int s, i;
1282de962bdSlukem
1292de962bdSlukem if (memcmp(eaddr, zero, sizeof(eaddr))) {
1302de962bdSlukem return eaddr;
1312de962bdSlukem }
1322de962bdSlukem
1332de962bdSlukem s = socket( AF_INET, SOCK_DGRAM, 0 );
1342de962bdSlukem if ( s < 0 ) {
1352de962bdSlukem return NULL;
1362de962bdSlukem }
1372de962bdSlukem
1382de962bdSlukem ifc.ifc_len = sizeof( buf );
1392de962bdSlukem ifc.ifc_buf = buf;
1402de962bdSlukem memset( buf, 0, sizeof( buf ) );
1412de962bdSlukem
1422de962bdSlukem i = ioctl( s, SIOCGIFCONF, (char *)&ifc );
1432de962bdSlukem close( s );
1442de962bdSlukem
1452de962bdSlukem if( i < 0 ) {
1462de962bdSlukem return NULL;
1472de962bdSlukem }
1482de962bdSlukem
1492de962bdSlukem for ( i = 0; i < ifc.ifc_len; ) {
1502de962bdSlukem ifr = (struct ifreq *)&ifc.ifc_buf[i];
1512de962bdSlukem sa = &ifr->ifr_addr;
1522de962bdSlukem
1532de962bdSlukem if ( sa->sa_len > sizeof( ifr->ifr_addr ) ) {
1542de962bdSlukem i += sizeof( ifr->ifr_name ) + sa->sa_len;
1552de962bdSlukem } else {
1562de962bdSlukem i += sizeof( *ifr );
1572de962bdSlukem }
1582de962bdSlukem
1592de962bdSlukem if ( sa->sa_family != AF_LINK ) {
1602de962bdSlukem continue;
1612de962bdSlukem }
1622de962bdSlukem
1632de962bdSlukem sdl = (struct sockaddr_dl *)sa;
1642de962bdSlukem
1652de962bdSlukem if ( sdl->sdl_alen == 6 ) {
1662de962bdSlukem AC_MEMCPY(eaddr,
1672de962bdSlukem (unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
1682de962bdSlukem sizeof(eaddr));
1692de962bdSlukem return eaddr;
1702de962bdSlukem }
1712de962bdSlukem }
1722de962bdSlukem
1732de962bdSlukem return NULL;
1742de962bdSlukem
1752de962bdSlukem #else
1762de962bdSlukem if (memcmp(eaddr, zero, sizeof(eaddr)) == 0) {
1772de962bdSlukem /* XXX - who knows? */
1782de962bdSlukem lutil_entropy( eaddr, sizeof(eaddr) );
1792de962bdSlukem eaddr[0] |= 0x01; /* turn it into a multicast address */
1802de962bdSlukem }
1812de962bdSlukem
1822de962bdSlukem return eaddr;
1832de962bdSlukem #endif
1842de962bdSlukem }
1852de962bdSlukem
1862de962bdSlukem #if (ULONG_MAX >> 31 >> 31) > 1 || defined HAVE_LONG_LONG
1872de962bdSlukem
1882de962bdSlukem #if (ULONG_MAX >> 31 >> 31) > 1
1892de962bdSlukem typedef unsigned long UI64;
1902de962bdSlukem /* 100 usec intervals from 10/10/1582 to 1/1/1970 */
1912de962bdSlukem # define UUID_TPLUS 0x01B21DD2138140ul
1922de962bdSlukem #else
1932de962bdSlukem typedef unsigned long long UI64;
1942de962bdSlukem # define UUID_TPLUS 0x01B21DD2138140ull
1952de962bdSlukem #endif
1962de962bdSlukem
1972de962bdSlukem #define high32(i) ((unsigned long) ((i) >> 32))
1982de962bdSlukem #define low32(i) ((unsigned long) (i) & 0xFFFFFFFFul)
1992de962bdSlukem #define set_add64(res, i) ((res) += (i))
2002de962bdSlukem #define set_add64l(res, i) ((res) += (i))
2012de962bdSlukem #define mul64ll(i1, i2) ((UI64) (i1) * (i2))
2022de962bdSlukem
2032de962bdSlukem #else /* ! (ULONG_MAX >= 64 bits || HAVE_LONG_LONG) */
2042de962bdSlukem
2052de962bdSlukem typedef struct {
2062de962bdSlukem unsigned long high, low;
2072de962bdSlukem } UI64;
2082de962bdSlukem
2092de962bdSlukem static const UI64 UUID_TPLUS = { 0x01B21Dul, 0xD2138140ul };
2102de962bdSlukem
2112de962bdSlukem #define high32(i) ((i).high)
2122de962bdSlukem #define low32(i) ((i).low)
2132de962bdSlukem
2142de962bdSlukem /* res += ui64 */
2152de962bdSlukem #define set_add64(res, ui64) \
2162de962bdSlukem { \
2172de962bdSlukem res.high += ui64.high; \
2182de962bdSlukem res.low = (res.low + ui64.low) & 0xFFFFFFFFul; \
2192de962bdSlukem if (res.low < ui64.low) res.high++; \
2202de962bdSlukem }
2212de962bdSlukem
2222de962bdSlukem /* res += ul32 */
2232de962bdSlukem #define set_add64l(res, ul32) \
2242de962bdSlukem { \
2252de962bdSlukem res.low = (res.low + ul32) & 0xFFFFFFFFul; \
2262de962bdSlukem if (res.low < ul32) res.high++; \
2272de962bdSlukem }
2282de962bdSlukem
2292de962bdSlukem /* compute i1 * i2 */
2302de962bdSlukem static UI64
mul64ll(unsigned long i1,unsigned long i2)2312de962bdSlukem mul64ll(unsigned long i1, unsigned long i2)
2322de962bdSlukem {
2332de962bdSlukem const unsigned int high1 = (i1 >> 16), low1 = (i1 & 0xffff);
2342de962bdSlukem const unsigned int high2 = (i2 >> 16), low2 = (i2 & 0xffff);
2352de962bdSlukem
2362de962bdSlukem UI64 res;
2372de962bdSlukem unsigned long tmp;
2382de962bdSlukem
2392de962bdSlukem res.high = (unsigned long) high1 * high2;
2402de962bdSlukem res.low = (unsigned long) low1 * low2;
2412de962bdSlukem
2422de962bdSlukem tmp = (unsigned long) low1 * high2;
2432de962bdSlukem res.high += (tmp >> 16);
2442de962bdSlukem tmp = (tmp << 16) & 0xFFFFFFFFul;
2452de962bdSlukem res.low = (res.low + tmp) & 0xFFFFFFFFul;
2462de962bdSlukem if (res.low < tmp)
2472de962bdSlukem res.high++;
2482de962bdSlukem
2492de962bdSlukem tmp = (unsigned long) low2 * high1;
2502de962bdSlukem res.high += (tmp >> 16);
2512de962bdSlukem tmp = (tmp << 16) & 0xFFFFFFFFul;
2522de962bdSlukem res.low = (res.low + tmp) & 0xFFFFFFFFul;
2532de962bdSlukem if (res.low < tmp)
2542de962bdSlukem res.high++;
2552de962bdSlukem
2562de962bdSlukem return res;
2572de962bdSlukem }
2582de962bdSlukem
2592de962bdSlukem #endif /* ULONG_MAX >= 64 bits || HAVE_LONG_LONG */
2602de962bdSlukem
2612de962bdSlukem #endif /* !HAVE_UUID_TO_STR && !HAVE_UUID_GENERATE && !_WIN32 */
2622de962bdSlukem
2632de962bdSlukem /*
2642de962bdSlukem ** All we really care about is an ISO UUID string. The format of a UUID is:
2652de962bdSlukem ** field octet note
2662de962bdSlukem ** time_low 0-3 low field of the timestamp
2672de962bdSlukem ** time_mid 4-5 middle field of timestamp
2682de962bdSlukem ** time_hi_and_version 6-7 high field of timestamp and
2692de962bdSlukem ** version number
2702de962bdSlukem ** clock_seq_hi_and_resv 8 high field of clock sequence
2712de962bdSlukem ** and variant
2722de962bdSlukem ** clock_seq_low 9 low field of clock sequence
2732de962bdSlukem ** node 10-15 spacially unique identifier
2742de962bdSlukem **
2752de962bdSlukem ** We use DCE version one, and the DCE variant. Our unique identifier is
2762de962bdSlukem ** the first ethernet address on the system.
2772de962bdSlukem */
2782de962bdSlukem size_t
lutil_uuidstr(char * buf,size_t len)2792de962bdSlukem lutil_uuidstr( char *buf, size_t len )
2802de962bdSlukem {
2812de962bdSlukem #ifdef HAVE_UUID_TO_STR
2822de962bdSlukem uuid_t uu = {0};
2832de962bdSlukem unsigned rc;
2842de962bdSlukem char *s;
2852de962bdSlukem size_t l;
2862de962bdSlukem
2872de962bdSlukem uuid_create( &uu, &rc );
2882de962bdSlukem if ( rc != uuid_s_ok ) {
2892de962bdSlukem return 0;
2902de962bdSlukem }
2912de962bdSlukem
2922de962bdSlukem uuid_to_str( &uu, &s, &rc );
2932de962bdSlukem if ( rc != uuid_s_ok ) {
2942de962bdSlukem return 0;
2952de962bdSlukem }
2962de962bdSlukem
2972de962bdSlukem l = strlen( s );
2982de962bdSlukem if ( l >= len ) {
2992de962bdSlukem free( s );
3002de962bdSlukem return 0;
3012de962bdSlukem }
3022de962bdSlukem
3032de962bdSlukem strncpy( buf, s, len );
3042de962bdSlukem free( s );
3052de962bdSlukem
3062de962bdSlukem return l;
3072de962bdSlukem
3082de962bdSlukem #elif defined( HAVE_UUID_GENERATE )
3092de962bdSlukem uuid_t uu;
3102de962bdSlukem
3112de962bdSlukem uuid_generate( uu );
3122de962bdSlukem uuid_unparse_lower( uu, buf );
3132de962bdSlukem return strlen( buf );
3142de962bdSlukem
3152de962bdSlukem #elif defined( _WIN32 )
3162de962bdSlukem UUID uuid;
3172de962bdSlukem unsigned char *uuidstr;
3182de962bdSlukem size_t uuidlen;
3192de962bdSlukem
3202de962bdSlukem if( UuidCreate( &uuid ) != RPC_S_OK ) {
3212de962bdSlukem return 0;
3222de962bdSlukem }
3232de962bdSlukem
3242de962bdSlukem if( UuidToString( &uuid, &uuidstr ) != RPC_S_OK ) {
3252de962bdSlukem return 0;
3262de962bdSlukem }
3272de962bdSlukem
3282de962bdSlukem uuidlen = strlen( uuidstr );
3292de962bdSlukem if( uuidlen >= len ) {
3302de962bdSlukem return 0;
3312de962bdSlukem }
3322de962bdSlukem
3332de962bdSlukem strncpy( buf, uuidstr, len );
3342de962bdSlukem RpcStringFree( &uuidstr );
3352de962bdSlukem
3362de962bdSlukem return uuidlen;
3372de962bdSlukem
3382de962bdSlukem #else
3392de962bdSlukem struct timeval tv;
3402de962bdSlukem UI64 tl;
3412de962bdSlukem unsigned char *nl;
3422de962bdSlukem unsigned short t2, t3, s1;
3432de962bdSlukem unsigned long t1, tl_high;
3442de962bdSlukem unsigned int rc;
3452de962bdSlukem
3462de962bdSlukem /*
3472de962bdSlukem * Theoretically we should delay if seq wraps within 100usec but for now
3482de962bdSlukem * systems are not fast enough to worry about it.
3492de962bdSlukem */
3502de962bdSlukem static int inited = 0;
3512de962bdSlukem static unsigned short seq;
3522de962bdSlukem
3532de962bdSlukem if (!inited) {
3542de962bdSlukem lutil_entropy( (unsigned char *) &seq, sizeof(seq) );
3552de962bdSlukem inited++;
3562de962bdSlukem }
3572de962bdSlukem
3582de962bdSlukem #ifdef HAVE_GETTIMEOFDAY
3592de962bdSlukem gettimeofday( &tv, 0 );
3602de962bdSlukem #else
3612de962bdSlukem time( &tv.tv_sec );
3622de962bdSlukem tv.tv_usec = 0;
3632de962bdSlukem #endif
3642de962bdSlukem
3652de962bdSlukem tl = mul64ll(tv.tv_sec, 10000000UL);
3662de962bdSlukem set_add64l(tl, tv.tv_usec * 10UL);
3672de962bdSlukem set_add64(tl, UUID_TPLUS);
3682de962bdSlukem
3692de962bdSlukem nl = lutil_eaddr();
3702de962bdSlukem
3712de962bdSlukem t1 = low32(tl); /* time_low */
3722de962bdSlukem tl_high = high32(tl);
3732de962bdSlukem t2 = tl_high & 0xffff; /* time_mid */
3742de962bdSlukem t3 = ((tl_high >> 16) & 0x0fff) | 0x1000; /* time_hi_and_version */
3752de962bdSlukem s1 = ( ++seq & 0x1fff ) | 0x8000; /* clock_seq_and_reserved */
3762de962bdSlukem
3772de962bdSlukem rc = snprintf( buf, len,
3782de962bdSlukem "%08lx-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
3792de962bdSlukem t1, (unsigned) t2, (unsigned) t3, (unsigned) s1,
3802de962bdSlukem (unsigned) nl[0], (unsigned) nl[1],
3812de962bdSlukem (unsigned) nl[2], (unsigned) nl[3],
3822de962bdSlukem (unsigned) nl[4], (unsigned) nl[5] );
3832de962bdSlukem
3842de962bdSlukem return rc < len ? rc : 0;
3852de962bdSlukem #endif
3862de962bdSlukem }
3872de962bdSlukem
3882de962bdSlukem int
lutil_uuidstr_from_normalized(char * uuid,size_t uuidlen,char * buf,size_t buflen)3892de962bdSlukem lutil_uuidstr_from_normalized(
3902de962bdSlukem char *uuid,
3912de962bdSlukem size_t uuidlen,
3922de962bdSlukem char *buf,
3932de962bdSlukem size_t buflen )
3942de962bdSlukem {
3952de962bdSlukem unsigned char nibble;
3962de962bdSlukem int i, d = 0;
3972de962bdSlukem
3982de962bdSlukem assert( uuid != NULL );
3992de962bdSlukem assert( buf != NULL );
4002de962bdSlukem
4012de962bdSlukem if ( uuidlen != 16 ) return -1;
4022de962bdSlukem if ( buflen < 36 ) return -1;
4032de962bdSlukem
4042de962bdSlukem for ( i = 0; i < 16; i++ ) {
4052de962bdSlukem if ( i == 4 || i == 6 || i == 8 || i == 10 ) {
4062de962bdSlukem buf[(i<<1)+d] = '-';
4072de962bdSlukem d += 1;
4082de962bdSlukem }
4092de962bdSlukem
4102de962bdSlukem nibble = (uuid[i] >> 4) & 0xF;
4112de962bdSlukem if ( nibble < 10 ) {
4122de962bdSlukem buf[(i<<1)+d] = nibble + '0';
4132de962bdSlukem } else {
4142de962bdSlukem buf[(i<<1)+d] = nibble - 10 + 'a';
4152de962bdSlukem }
4162de962bdSlukem
4172de962bdSlukem nibble = (uuid[i]) & 0xF;
4182de962bdSlukem if ( nibble < 10 ) {
4192de962bdSlukem buf[(i<<1)+d+1] = nibble + '0';
4202de962bdSlukem } else {
4212de962bdSlukem buf[(i<<1)+d+1] = nibble - 10 + 'a';
4222de962bdSlukem }
4232de962bdSlukem }
4242de962bdSlukem
4252de962bdSlukem if ( buflen > 36 ) buf[36] = '\0';
4262de962bdSlukem return 36;
4272de962bdSlukem }
4282de962bdSlukem
4292de962bdSlukem #ifdef TEST
4302de962bdSlukem int
main(int argc,char ** argv)4312de962bdSlukem main(int argc, char **argv)
4322de962bdSlukem {
4332de962bdSlukem char buf1[8], buf2[64];
4342de962bdSlukem
4352de962bdSlukem #ifndef HAVE_UUID_TO_STR
4362de962bdSlukem unsigned char *p = lutil_eaddr();
4372de962bdSlukem
4382de962bdSlukem if( p ) {
4392de962bdSlukem printf( "Ethernet Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
4402de962bdSlukem (unsigned) p[0], (unsigned) p[1], (unsigned) p[2],
4412de962bdSlukem (unsigned) p[3], (unsigned) p[4], (unsigned) p[5]);
4422de962bdSlukem }
4432de962bdSlukem #endif
4442de962bdSlukem
4452de962bdSlukem if ( lutil_uuidstr( buf1, sizeof( buf1 ) ) ) {
4462de962bdSlukem printf( "UUID: %s\n", buf1 );
4472de962bdSlukem } else {
4482de962bdSlukem fprintf( stderr, "too short: %ld\n", (long) sizeof( buf1 ) );
4492de962bdSlukem }
4502de962bdSlukem
4512de962bdSlukem if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
4522de962bdSlukem printf( "UUID: %s\n", buf2 );
4532de962bdSlukem } else {
4542de962bdSlukem fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
4552de962bdSlukem }
4562de962bdSlukem
4572de962bdSlukem if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
4582de962bdSlukem printf( "UUID: %s\n", buf2 );
4592de962bdSlukem } else {
4602de962bdSlukem fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
4612de962bdSlukem }
4622de962bdSlukem
4632de962bdSlukem return 0;
4642de962bdSlukem }
4652de962bdSlukem #endif
466