]> git.sur5r.net Git - openldap/blob - libraries/liblutil/uuid.c
fix snprintf result handling
[openldap] / libraries / liblutil / uuid.c
1 /*
2  * Copyright 1998-2002 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 <stdio.h>
21 #include <sys/types.h>
22
23 #include <ac/stdlib.h>
24
25 #ifdef HAVE_UUID_TO_STR
26 #  include <sys/uuid.h>
27 #elif defined( _WIN32 )
28 #  include <rpc.h>
29 #else
30 #  include <ac/socket.h>
31 #  include <ac/time.h>
32
33         /* 100 usec intervals from 10/10/1582 to 1/1/1970 */
34 #       define UUID_TPLUS       0x01B21DD2138140LL
35
36 #  ifdef HAVE_SYS_SYSCTL_H
37 #    include <net/if.h>
38 #    include <sys/sysctl.h>
39 #    include <net/route.h>
40 #  endif
41 #endif
42
43 #include <lutil.h>
44
45 /* not needed for Windows */
46 #if !defined(HAVE_UUID_TO_STR) && !defined(_WIN32)
47 static unsigned char *
48 lutil_eaddr( void )
49 {
50         static unsigned char zero[6];
51         static unsigned char eaddr[6];
52
53 #ifdef HAVE_SYS_SYSCTL_H
54         size_t needed;
55         int mib[6];
56         char *buf, *next, *lim;
57         struct if_msghdr *ifm;
58         struct sockaddr_dl *sdl;
59
60         if (memcmp(eaddr, zero, sizeof(eaddr))) {
61                 return eaddr;
62         }
63
64         mib[0] = CTL_NET;
65         mib[1] = PF_ROUTE;
66         mib[3] = 0;
67         mib[3] = 0;
68         mib[4] = NET_RT_IFLIST;
69         mib[5] = 0;
70
71         if (sysctl(mib, sizeof(mib), NULL, &needed, NULL, 0) < 0) {
72                 return NULL;
73         }
74
75         buf = malloc(needed);
76         if( buf == NULL ) return NULL;
77
78         if (sysctl(mib, sizeof(mib), buf, &needed, NULL, 0) < 0) {
79                 free(buf);
80                 return NULL;
81         }
82
83         lim = buf + needed;
84         for (next = buf; next < lim; next += ifm->ifm_msglen) {
85                 ifm = (struct if_msghdr *)next;
86                 sdl = (struct sockaddr_dl *)(ifm + 1);
87
88                 if ( sdl->sdl_family != AF_LINK || sdl->sdl_alen == 6 ) {
89                         AC_MEMCPY(eaddr,
90                                 (unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
91                                 sizeof(eaddr));
92                         free(buf);
93                         return eaddr;
94                 }
95         }
96
97         free(buf);
98         return NULL;
99
100 #elif defined( SIOCGIFADDR ) && defined( AFLINK )
101         char buf[sizeof(struct ifreq) * 32];
102         struct ifconf ifc;
103         struct ifreq *ifr;
104         struct sockaddr *sa;
105         struct sockaddr_dl *sdl;
106         unsigned char *p;
107         int s, i;
108
109         if (memcmp(eaddr, zero, sizeof(eaddr))) {
110                 return eaddr;
111         }
112
113         s = socket( AF_INET, SOCK_DGRAM, 0 );
114         if ( s < 0 ) {
115                 return NULL;
116         }
117
118         ifc.ifc_len = sizeof( buf );
119         ifc.ifc_buf = buf;
120         memset( buf, 0, sizeof( buf ) );
121
122         i = ioctl( s, SIOCGIFCONF, (char *)&ifc );
123         close( s );
124
125         if( i < 0 ) {
126                 return NULL;
127         }
128
129         for ( i = 0; i < ifc.ifc_len; ) {
130                 ifr = (struct ifreq *)&ifc.ifc_buf[i];
131                 sa = &ifr->ifr_addr;
132
133                 if ( sa->sa_len > sizeof( ifr->ifr_addr ) ) {
134                         i += sizeof( ifr->ifr_name ) + sa->sa_len;
135                 } else {
136                         i += sizeof( *ifr );
137                 }
138
139                 if ( sa->sa_family != AF_LINK ) {
140                         continue;
141                 }
142
143                 sdl = (struct sockaddr_dl *)sa;
144
145                 if ( sdl->sdl_alen == 6 ) {
146                         AC_MEMCPY(eaddr,
147                                 (unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
148                                 sizeof(eaddr));
149                         return eaddr;
150                 }
151         }
152
153         return NULL;
154
155 #else
156         if (memcmp(eaddr, zero, sizeof(eaddr)) == 0) {
157                 /* XXX - who knows? */
158                 lutil_entropy( eaddr, sizeof(eaddr) );
159                 eaddr[0] |= 0x80; /* turn it into a multicast address */
160         }
161
162         return eaddr;
163 #endif
164 }
165 #endif
166
167 /*
168 ** All we really care about is an ISO UUID string.  The format of a UUID is:
169 **      field                   octet           note
170 **      time_low                0-3             low field of the timestamp
171 **      time_mid                4-5             middle field of timestamp
172 **      time_hi_and_version     6-7             high field of timestamp and
173 **                                              version number
174 **      clock_seq_hi_and_resv   8               high field of clock sequence
175 **                                              and variant
176 **      clock_seq_low           9               low field of clock sequence
177 **      node                    10-15           spacially unique identifier
178 **
179 ** We use DCE version one, and the DCE variant.  Our unique identifier is
180 ** the first ethernet address on the system.
181 */
182 size_t
183 lutil_uuidstr( char *buf, size_t len )
184 {
185 #ifdef HAVE_UUID_TO_STR
186         uuid_t uu = {0};
187         unsigned rc;
188         char *s;
189         size_t l;
190
191         uuid_create( &uu, &rc );
192         if ( rc != uuid_s_ok ) {
193                 return 0;
194         }
195
196         uuid_to_str( &uu, &s, &rc );
197         if ( rc != uuid_s_ok ) {
198                 return 0;
199         }
200
201         l = strlen( s );
202         if ( l >= len ) {
203                 free( s );
204                 return 0;
205         }
206
207         strncpy( buf, s, len );
208         free( s );
209
210         return l;
211
212 #elif defined( _WIN32 )
213         UUID uuid;
214         unsigned char *uuidstr;
215         size_t uuidlen;
216
217         if( UuidCreate( &uuid ) != RPC_S_OK ) {
218                 return 0;
219         }
220  
221         if( UuidToString( &uuid, &uuidstr ) !=  RPC_S_OK ) {
222                 return 0;
223         }
224
225         uuidlen = strlen( uuidstr );
226         if( uuidlen >= len ) {
227                 return 0;
228         }
229
230         strncpy( buf, uuidstr, len );
231         RpcStringFree( &uuidstr );
232
233         return uuidlen;
234  
235 #else
236         struct timeval tv;
237         unsigned long long tl;
238         unsigned char *nl;
239         unsigned short t2, t3, s1;
240         unsigned int t1;
241
242         /*
243          * Theoretically we should delay if seq wraps within 100usec but for now
244          * systems are not fast enough to worry about it.
245          */
246         static int inited = 0;
247         static unsigned short seq;
248         
249         if (!inited) {
250                 lutil_entropy( (unsigned char *) &seq, sizeof(seq) );
251                 inited++;
252         }
253
254 #ifdef HAVE_GETTIMEOFDAY
255         gettimeofday( &tv, 0 );
256 #else
257         time( &tv.tv_sec );
258         tv.tv_usec = 0;
259 #endif
260
261         tl = ( tv.tv_sec * 10000000LL ) + ( tv.tv_usec * 10LL ) + UUID_TPLUS;
262         nl = lutil_eaddr();
263
264         t1 = tl & 0xffffffff;                                   /* time_low */
265         t2 = ( tl >> 32 ) & 0xffff;                             /* time_mid */
266         t3 = ( ( tl >> 48 ) & 0x0fff ) | 0x1000;        /* time_hi_and_version */
267         s1 = ( ++seq & 0x1fff ) | 0x8000;               /* clock_seq_and_reserved */
268
269         t1 = snprintf( buf, len,
270                 "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
271             t1, (unsigned) t2, (unsigned) t3, (unsigned) s1,
272                 (unsigned) nl[0], (unsigned) nl[1],
273                 (unsigned) nl[2], (unsigned) nl[3],
274                 (unsigned) nl[4], (unsigned) nl[5] );
275
276         return (0 < t1 && t1 < len) ? t1 : 0;
277 #endif
278 }
279
280 #ifdef TEST
281 int
282 main(int argc, char **argv)
283 {
284         char buf1[8], buf2[64];
285
286 #ifndef HAVE_UUID_TO_STR
287         unsigned char *p = lutil_eaddr();
288
289         if( p ) {
290                 printf( "Ethernet Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
291                         (unsigned) p[0], (unsigned) p[1], (unsigned) p[2],
292                         (unsigned) p[3], (unsigned) p[4], (unsigned) p[5]);
293         }
294 #endif
295
296         if ( lutil_uuidstr( buf1, sizeof( buf1 ) ) ) {
297                 printf( "UUID: %s\n", buf1 );
298         } else {
299                 fprintf( stderr, "too short: %ld\n", (long) sizeof( buf1 ) );
300         }
301
302         if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
303                 printf( "UUID: %s\n", buf2 );
304         } else {
305                 fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
306         }
307
308         if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
309                 printf( "UUID: %s\n", buf2 );
310         } else {
311                 fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
312         }
313
314         return 0;
315 }
316 #endif