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