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