1cf9ba84a6b9e198ef88708a7366c5e7776b0fa4
[public/netxms.git] / src / libnetxms / gen_uuid.c
1 /*
2 * gen_uuid.c --- generate a DCE-compatible uuid
3 *
4 * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU
8 * Library General Public License.
9 * %End-Header%
10 */
11
12 /*
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <sys/time.h>
20 #include <sys/stat.h>
21 #include <sys/file.h>
22 #include <sys/ioctl.h>
23 #include <sys/socket.h>
24 #ifdef HAVE_SYS_SOCKIO_H
25 #include <sys/sockio.h>
26 #endif
27 #ifdef HAVE_NET_IF_H
28 #include <net/if.h>
29 #endif
30 #ifdef HAVE_NETINET_IN_H
31 #include <netinet/in.h>
32 #endif
33 */
34
35 #include "libnetxms.h"
36 #include "uuidP.h"
37
38 #if HAVE_SYS_IOCTL_H
39 #include <sys/ioctl.h>
40 #endif
41 #if HAVE_SYS_SOCKIO_H
42 #include <sys/sockio.h>
43 #endif
44 #if HAVE_NET_IF_H
45 #include <net/if.h>
46 #endif
47 #if HAVE_NET_IF_ARP_H
48 #include <net/if_arp.h>
49 #endif
50 #if HAVE_NET_IF_DL_H
51 #include <net/if_dl.h>
52 #endif
53
54 #ifdef HAVE_SRANDOM
55 #define srand(x) srandom(x)
56 #define rand() random()
57 #endif
58
59 #ifndef _WIN32
60
61 static int get_random_fd()
62 {
63 int fd = -2;
64 int i;
65
66 if (fd == -2)
67 {
68 fd = open("/dev/urandom", O_RDONLY);
69 if (fd == -1)
70 fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
71 srand((getpid() << 16) ^ getuid() ^ time(0));
72 }
73 /* Crank the random number generator a few times */
74 for (i = time(0) & 0x1F; i > 0; i--)
75 rand();
76 return fd;
77 }
78
79 #endif
80
81
82 /*
83 * Generate a series of random bytes. Use /dev/urandom if possible,
84 * and if not, use srandom/random.
85 */
86
87 static void get_random_bytes(void *buf, int nbytes)
88 {
89 int i;
90 char *cp = (char *)buf;
91 #ifndef _WIN32
92 int fd = get_random_fd();
93 int lose_counter = 0;
94
95 if (fd >= 0)
96 {
97 while (nbytes > 0)
98 {
99 i = read(fd, cp, nbytes);
100 if ((i < 0) &&
101 ((errno == EINTR) || (errno == EAGAIN)))
102 continue;
103 if (i <= 0) {
104 if (lose_counter++ == 8)
105 break;
106 continue;
107 }
108 nbytes -= i;
109 cp += i;
110 lose_counter = 0;
111 }
112 close(fd);
113 }
114 #endif
115 if (nbytes == 0)
116 return;
117
118 /* XXX put something better here if no /dev/random! */
119 for (i=0; i < nbytes; i++)
120 *cp++ = rand() & 0xFF;
121 return;
122 }
123
124 /*
125 * Get the ethernet hardware address, if we can find it...
126 */
127 static int get_node_id(unsigned char *node_id)
128 {
129 #ifdef HAVE_NET_IF_H
130 int sd;
131 struct ifreq ifr, *ifrp;
132 struct ifconf ifc;
133 char buf[1024];
134 int n, i;
135 unsigned char *a;
136
137 /*
138 * BSD 4.4 defines the size of an ifreq to be
139 * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
140 * However, under earlier systems, sa_len isn't present, so the size is
141 * just sizeof(struct ifreq)
142 */
143 #ifdef HAVE_SA_LEN
144 #ifndef max
145 #define max(a,b) ((a) > (b) ? (a) : (b))
146 #endif
147 #define ifreq_size(i) max(sizeof(struct ifreq),\
148 sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
149 #else
150 #define ifreq_size(i) sizeof(struct ifreq)
151 #endif /* HAVE_SA_LEN*/
152
153 #ifdef AF_INET6
154 sd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
155 if (sd < 0)
156 #endif
157 {
158 sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
159 if (sd < 0)
160 {
161 return -1;
162 }
163 }
164 memset(buf, 0, sizeof(buf));
165 ifc.ifc_len = sizeof(buf);
166 ifc.ifc_buf = buf;
167 if (ioctl(sd, SIOCGIFCONF, (char *)&ifc) < 0)
168 {
169 close(sd);
170 return -1;
171 }
172 n = ifc.ifc_len;
173 for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
174 ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
175 strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
176 #ifdef SIOCGIFHWADDR
177 if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
178 continue;
179 a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
180 #else
181 #ifdef SIOCGENADDR
182 if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
183 continue;
184 a = (unsigned char *) ifr.ifr_enaddr;
185 #else
186 /*
187 * XXX we don't have a way of getting the hardware
188 * address
189 */
190 close(sd);
191 return 0;
192 #endif /* SIOCGENADDR */
193 #endif /* SIOCGIFHWADDR */
194 if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
195 continue;
196 if (node_id) {
197 memcpy(node_id, a, 6);
198 close(sd);
199 return 1;
200 }
201 }
202 close(sd);
203 #endif
204 return 0;
205 }
206
207 /* Assume that the gettimeofday() has microsecond granularity */
208 #define MAX_ADJUSTMENT 10
209
210 static int get_clock(DWORD *clock_high, DWORD *clock_low, WORD *ret_clock_seq)
211 {
212 static int adjustment = 0;
213 static struct timeval last = {0, 0};
214 static unsigned short clock_seq;
215 struct timeval tv;
216 QWORD clock_reg;
217 #ifdef _WIN32
218 FILETIME ft;
219 LARGE_INTEGER li;
220 __int64 t;
221 #endif
222
223 try_again:
224 #ifdef _WIN32
225 GetSystemTimeAsFileTime(&ft);
226 li.LowPart = ft.dwLowDateTime;
227 li.HighPart = ft.dwHighDateTime;
228 t = li.QuadPart; // In 100-nanosecond intervals
229 t /= 10; // To microseconds
230 tv.tv_sec = (time_t)(t / 1000000);
231 tv.tv_usec = (time_t)(t % 1000000);
232 #else
233 gettimeofday(&tv, NULL);
234 #endif
235 if ((last.tv_sec == 0) && (last.tv_usec == 0))
236 {
237 get_random_bytes(&clock_seq, sizeof(clock_seq));
238 clock_seq &= 0x1FFF;
239 last = tv;
240 last.tv_sec--;
241 }
242 if ((tv.tv_sec < last.tv_sec) ||
243 ((tv.tv_sec == last.tv_sec) &&
244 (tv.tv_usec < last.tv_usec)))
245 {
246 clock_seq = (clock_seq + 1) & 0x1FFF;
247 adjustment = 0;
248 last = tv;
249 }
250 else if ((tv.tv_sec == last.tv_sec) &&
251 (tv.tv_usec == last.tv_usec))
252 {
253 if (adjustment >= MAX_ADJUSTMENT)
254 goto try_again;
255 adjustment++;
256 }
257 else
258 {
259 adjustment = 0;
260 last = tv;
261 }
262
263 clock_reg = tv.tv_usec * 10 + adjustment;
264 clock_reg += ((QWORD)tv.tv_sec) * 10000000;
265 clock_reg += (((QWORD)0x01B21DD2) << 32) + 0x13814000;
266
267 *clock_high = (DWORD)(clock_reg >> 32);
268 *clock_low = (DWORD)clock_reg;
269 *ret_clock_seq = clock_seq;
270 return 0;
271 }
272
273 static void uuid_generate_time(uuid_t out)
274 {
275 static unsigned char node_id[6];
276 static int has_init = 0;
277 struct uuid uu;
278 DWORD clock_mid;
279
280 if (!has_init)
281 {
282 if (get_node_id(node_id) <= 0)
283 {
284 get_random_bytes(node_id, 6);
285 /*
286 * Set multicast bit, to prevent conflicts
287 * with IEEE 802 addresses obtained from
288 * network cards
289 */
290 node_id[0] |= 0x80;
291 }
292 has_init = 1;
293 }
294 get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
295 uu.clock_seq |= 0x8000;
296 uu.time_mid = (WORD)clock_mid;
297 uu.time_hi_and_version = (WORD)((clock_mid >> 16) | 0x1000);
298 memcpy(uu.node, node_id, 6);
299 uuid_pack(&uu, out);
300 }
301
302 static void uuid_generate_random(uuid_t out)
303 {
304 uuid_t buf;
305 struct uuid uu;
306
307 get_random_bytes(buf, sizeof(buf));
308 uuid_unpack(buf, &uu);
309
310 uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
311 uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
312 uuid_pack(&uu, out);
313 }
314
315 /*
316 * This is the generic front-end to uuid_generate_random and
317 * uuid_generate_time. It uses uuid_generate_random only if
318 * /dev/urandom is available, since otherwise we won't have
319 * high-quality randomness.
320 */
321 void LIBNETXMS_EXPORTABLE uuid_generate(uuid_t out)
322 {
323 #ifndef _WIN32
324 int fd;
325
326 fd = get_random_fd();
327 if (fd >= 0)
328 {
329 close(fd);
330 uuid_generate_random(out);
331 }
332 else
333 #endif
334 uuid_generate_time(out);
335 }