]> git.sur5r.net Git - openldap/blob - libraries/liblutil/uuid.c
b6286f0589de1545bd91233743e9f95abb2edade
[openldap] / libraries / liblutil / uuid.c
1 /*
2  * Copyright 1998-2000 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 /* Adapted for incorporatation into OpenLDAP by Kurt Zeilenga */
13
14 /*
15  * Sorry this file is so scary, but it needs to run on a wide range of
16  * platforms.  The only exported routine is lutil_uuidstr() which is all
17  * that LDAP cares about.  It generates a new uuid and returns it in
18  * in string form.
19  */
20 #include "portable.h"
21
22 #include <stdio.h>
23 #include <sys/types.h>
24
25 #ifdef HAVE_SYS_UUID_H
26 #  include <sys/uuid.h>
27 #else
28 #  include <ac/socket.h>
29 #  include <sys/sockio.h>
30 #  include <net/if_types.h>
31 #  include <net/if.h>
32 #  include <net/if_dl.h>
33 #  ifdef HAVE_SYS_SYSCTL_H
34 #    include <sys/sysctl.h>
35 #    include <net/route.h>
36 #  endif
37 #endif
38
39 #include <lutil.h>
40
41 /* 100 usec intervals from 10/10/1582 to 1/1/1970 */
42 #define UUID_TPLUS      0x01B21DD2138140LL
43
44 #ifndef HAVE_SYS_UUID_H
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)
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 mutlicast address */
158         }
159
160         return eaddr;
161 #endif
162 }
163 #endif
164
165 /*
166 ** All we really care about is an ISO UUID string.  The format of a UUID is:
167 **      field                   octet           note
168 **      time_low                0-3             low field of the timestamp
169 **      time_mid                4-5             middle field of timestamp
170 **      time_hi_and_version     6-7             high field of timestamp and
171 **                                              version number
172 **      clock_seq_hi_and_resv   8               high field of clock sequence
173 **                                              and variant
174 **      clock_seq_low           9               low field of clock sequence
175 **      node                    10-15           spacially unique identifier
176 **
177 ** We use DCE version one, and the DCE variant.  Our unique identifier is
178 ** the first ethernet address on the system.
179 */
180 int
181 lutil_uuidstr( char *buf, size_t len )
182 {
183 #ifdef HAVE_SYS_UUID_H
184         uuid_t uu = {0};
185         unsigned rc;
186         char *s;
187         size_t l;
188
189         uuid_create( &uu, &rc );
190         if ( rc != uuid_s_ok ) {
191                 return 0;
192         }
193
194         uuid_to_str( &uu, &s, &rc );
195         if ( rc != uuid_s_ok ) {
196                 return 0;
197         }
198
199         l = strlen( s );
200         if ( l >= len ) {
201                 free( s );
202                 return 0;
203         }
204
205         strncpy( buf, s, l );
206         free( s );
207
208         return l;
209
210 #else
211         struct timeval tv;
212         unsigned long long tl;
213         unsigned char *nl;
214         unsigned short t2, t3, s1, n1, n2, n3;
215         unsigned int t1;
216
217         /*
218          * Theoretically we should delay if seq wraps within 100usec but for now
219          * systems are not fast enough to worry about it.
220          */
221         static int inited = 0;
222         static unsigned short seq;
223         
224         if (!inited) {
225                 lutil_entropy( (unsigned char *) &seq, sizeof(seq) );
226                 inited++;
227         }
228
229 #ifdef HAVE_GETTIMEOFDAY
230         gettimeofday( &tv, 0 );
231 #else
232         time( &tv.tv_sec );
233         tv.tv_usec = 0;
234 #endif
235
236         tl = ( tv.tv_sec * 10000000LL ) + ( tv.tv_usec * 10LL ) + UUID_TPLUS;
237         nl = lutil_eaddr();
238
239         t1 = tl & 0xffffffff;                                   /* time_low */
240         t2 = ( tl >> 32 ) & 0xffff;                             /* time_mid */
241         t3 = ( ( tl >> 48 ) & 0x0fff ) | 0x1000;        /* time_hi_and_version */
242         s1 = ( ++seq & 0x1fff ) | 0x8000;               /* clock_seq_and_reserved */
243
244         t1 = snprintf( buf, len,
245                 "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
246             t1, (unsigned) t2, (unsigned) t3, (unsigned) s1,
247                 (unsigned) nl[0], (unsigned) nl[1],
248                 (unsigned) nl[2], (unsigned) nl[3],
249                 (unsigned) nl[4], (unsigned) nl[5] );
250
251         return (t1 < len) ? t1 : 0;
252 #endif
253 }
254
255 #ifdef TEST
256 int
257 main(int argc, char **argv)
258 {
259         char buf1[8], buf2[64];
260
261 #ifndef HAVE_SYS_UUID_H
262         unsigned char *p = lutil_eaddr();
263
264         if( p ) {
265                 printf( "Ethernet Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
266                         (unsigned) p[0], (unsigned) p[1], (unsigned) p[2],
267                         (unsigned) p[3], (unsigned) p[4], (unsigned) p[5]);
268         }
269 #endif
270
271         if ( lutil_uuidstr( buf1, sizeof( buf1 ) ) ) {
272                 printf( "UUID: %s\n", buf1 );
273         } else {
274                 fprintf( stderr, "too short: %ld\n", (long) sizeof( buf1 ) );
275         }
276
277         if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
278                 printf( "UUID: %s\n", buf2 );
279         } else {
280                 fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
281         }
282
283         if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
284                 printf( "UUID: %s\n", buf2 );
285         } else {
286                 fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
287         }
288
289         return 0;
290 }
291 #endif