9e5ec8ae8f341a6f9c091d03980cab3cfd88141f
[public/netxms.git] / src / libnetxms / uuid.cpp
1 /*
2 ** libuuid integrated into NetXMS project
3 ** Copyright (C) 1996, 1997 Theodore Ts'o.
4 ** Integrated into NetXMS by Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published
8 ** by the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: uuid.cpp
21 **
22 **/
23
24 #include "libnetxms.h"
25 #include <uuid.h>
26
27 #if HAVE_SYS_IOCTL_H
28 #include <sys/ioctl.h>
29 #endif
30
31 #if HAVE_SYS_SOCKIO_H
32 #include <sys/sockio.h>
33 #endif
34
35 #if HAVE_NET_IF_H
36 #include <net/if.h>
37 #endif
38
39 #if HAVE_NET_IF_ARP_H
40 #include <net/if_arp.h>
41 #endif
42
43 #if HAVE_NET_IF_DL_H
44 #include <net/if_dl.h>
45 #endif
46
47 #if HAVE_SYS_UTSNAME_H
48 #include <sys/utsname.h>
49 #endif
50
51 #ifdef HAVE_SRANDOM
52 #define srand(x) srandom(x)
53 #define rand() random()
54 #endif
55
56 /**
57 * NULL GUID
58 */
59 const uuid uuid::NULL_UUID = uuid();
60
61 /*
62 * Offset between 15-Oct-1582 and 1-Jan-70
63 */
64 #define TIME_OFFSET_HIGH 0x01B21DD2
65 #define TIME_OFFSET_LOW 0x13814000
66
67 /**
68 * Unpacked UUID structure
69 */
70 struct __uuid
71 {
72 DWORD time_low;
73 WORD time_mid;
74 WORD time_hi_and_version;
75 WORD clock_seq;
76 BYTE node[6];
77 };
78
79 /**
80 * Internal routine for packing UUID's
81 */
82 static void uuid_pack(struct __uuid *uu, uuid_t ptr)
83 {
84 unsigned int tmp;
85 unsigned char *out = ptr;
86
87 tmp = uu->time_low;
88 out[3] = (unsigned char) tmp;
89 tmp >>= 8;
90 out[2] = (unsigned char) tmp;
91 tmp >>= 8;
92 out[1] = (unsigned char) tmp;
93 tmp >>= 8;
94 out[0] = (unsigned char) tmp;
95
96 tmp = uu->time_mid;
97 out[5] = (unsigned char) tmp;
98 tmp >>= 8;
99 out[4] = (unsigned char) tmp;
100
101 tmp = uu->time_hi_and_version;
102 out[7] = (unsigned char) tmp;
103 tmp >>= 8;
104 out[6] = (unsigned char) tmp;
105
106 tmp = uu->clock_seq;
107 out[9] = (unsigned char) tmp;
108 tmp >>= 8;
109 out[8] = (unsigned char) tmp;
110
111 memcpy(out+10, uu->node, 6);
112 }
113
114 /**
115 * Internal routine for unpacking UUID
116 */
117 static void uuid_unpack(const uuid_t in, struct __uuid *uu)
118 {
119 const unsigned char *ptr = in;
120 unsigned int tmp;
121
122 tmp = *ptr++;
123 tmp = (tmp << 8) | *ptr++;
124 tmp = (tmp << 8) | *ptr++;
125 tmp = (tmp << 8) | *ptr++;
126 uu->time_low = tmp;
127
128 tmp = *ptr++;
129 tmp = (tmp << 8) | *ptr++;
130 uu->time_mid = tmp;
131
132 tmp = *ptr++;
133 tmp = (tmp << 8) | *ptr++;
134 uu->time_hi_and_version = tmp;
135
136 tmp = *ptr++;
137 tmp = (tmp << 8) | *ptr++;
138 uu->clock_seq = tmp;
139
140 memcpy(uu->node, ptr, 6);
141 }
142
143 /**
144 * Clear a UUID
145 */
146 void LIBNETXMS_EXPORTABLE uuid_clear(uuid_t uu)
147 {
148 memset(uu, 0, 16);
149 }
150
151 #define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1);
152
153 /**
154 * compare whether or not two UUID's are the same
155 *
156 * Returns 1/-1 if the two UUID's are different, and 0 if they are the same.
157 */
158 int LIBNETXMS_EXPORTABLE uuid_compare(const uuid_t uu1, const uuid_t uu2)
159 {
160 struct __uuid uuid1, uuid2;
161
162 uuid_unpack(uu1, &uuid1);
163 uuid_unpack(uu2, &uuid2);
164
165 UUCMP(uuid1.time_low, uuid2.time_low);
166 UUCMP(uuid1.time_mid, uuid2.time_mid);
167 UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version);
168 UUCMP(uuid1.clock_seq, uuid2.clock_seq);
169 return memcmp(uuid1.node, uuid2.node, 6);
170 }
171
172 /**
173 * isnull.c --- Check whether or not the UUID is null
174 * Returns true if the uuid is the NULL uuid
175 */
176 bool LIBNETXMS_EXPORTABLE uuid_is_null(const uuid_t uu)
177 {
178 const unsigned char *cp;
179 int i;
180
181 for (i=0, cp = uu; i < 16; i++)
182 if (*cp++)
183 return false;
184 return true;
185 }
186
187 /**
188 * Parse UUID
189 */
190 int LIBNETXMS_EXPORTABLE uuid_parse(const TCHAR *in, uuid_t uu)
191 {
192 struct __uuid uuid;
193 int i;
194 const TCHAR *cp;
195 TCHAR buf[3];
196
197 if (_tcslen(in) != 36)
198 return -1;
199 for (i=0, cp = in; i <= 36; i++,cp++) {
200 if ((i == 8) || (i == 13) || (i == 18) ||
201 (i == 23))
202 if (*cp == _T('-'))
203 continue;
204 if (i == 36)
205 if (*cp == 0)
206 continue;
207 if (!_istxdigit(*cp))
208 return -1;
209 }
210 uuid.time_low = _tcstoul(in, NULL, 16);
211 uuid.time_mid = (WORD)_tcstoul(in + 9, NULL, 16);
212 uuid.time_hi_and_version = (WORD)_tcstoul(in + 14, NULL, 16);
213 uuid.clock_seq = (WORD)_tcstoul(in + 19, NULL, 16);
214 cp = in + 24;
215 buf[2] = 0;
216 for(i = 0; i < 6; i++)
217 {
218 buf[0] = *cp++;
219 buf[1] = *cp++;
220 uuid.node[i] = (BYTE)_tcstoul(buf, NULL, 16);
221 }
222
223 uuid_pack(&uuid, uu);
224 return 0;
225 }
226
227 /**
228 * Convert packed UUID to string
229 */
230 TCHAR LIBNETXMS_EXPORTABLE *uuid_to_string(const uuid_t uu, TCHAR *out)
231 {
232 struct __uuid uuid;
233
234 uuid_unpack(uu, &uuid);
235 _sntprintf(out, 64,
236 _T("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"),
237 uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
238 uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
239 uuid.node[0], uuid.node[1], uuid.node[2],
240 uuid.node[3], uuid.node[4], uuid.node[5]);
241 return out;
242 }
243
244 #ifndef _WIN32
245
246 static int get_random_fd()
247 {
248 int fd = -2;
249 int i;
250
251 if (fd == -2)
252 {
253 fd = open("/dev/urandom", O_RDONLY);
254 if (fd == -1)
255 fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
256 srand((getpid() << 16) ^ getuid() ^ time(0));
257 }
258 /* Crank the random number generator a few times */
259 for (i = time(0) & 0x1F; i > 0; i--)
260 rand();
261 return fd;
262 }
263
264 #endif
265
266 /*
267 * Generate a series of random bytes. Use /dev/urandom if possible,
268 * and if not, use srandom/random.
269 */
270 static void get_random_bytes(void *buf, int nbytes)
271 {
272 int i;
273 char *cp = (char *)buf;
274 #ifndef _WIN32
275 int fd = get_random_fd();
276 int lose_counter = 0;
277
278 if (fd >= 0)
279 {
280 while (nbytes > 0)
281 {
282 i = read(fd, cp, nbytes);
283 if ((i < 0) &&
284 ((errno == EINTR) || (errno == EAGAIN)))
285 continue;
286 if (i <= 0) {
287 if (lose_counter++ == 8)
288 break;
289 continue;
290 }
291 nbytes -= i;
292 cp += i;
293 lose_counter = 0;
294 }
295 close(fd);
296 }
297 #endif
298 if (nbytes == 0)
299 return;
300
301 /* FIXME: put something better here if no /dev/random! */
302 srand((unsigned int)time(NULL) ^ getpid());
303 for (i = 0; i < nbytes; i++)
304 *cp++ = rand() & 0xFF;
305 return;
306 }
307
308 /*
309 * Get the ethernet hardware address or other available node ID
310 */
311 static int get_node_id(unsigned char *node_id)
312 {
313 #if defined(_AIX)
314
315 struct utsname info;
316 if (uname(&info) == -1)
317 return -1;
318
319 memset(node_id, 0, 6);
320 StrToBinA(info.machine, node_id, 6);
321 return 0;
322
323 #elif defined(HAVE_NET_IF_H) && !defined(sun) && !defined(__sun) && (defined(SIOCGIFHWADDR) || defined(SIOCGENADDR))
324
325 int sd;
326 struct ifreq ifr, *ifrp;
327 struct ifconf ifc;
328 char buf[1024];
329 int n, i;
330 unsigned char *a;
331
332 /*
333 * BSD 4.4 defines the size of an ifreq to be
334 * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
335 * However, under earlier systems, sa_len isn't present, so the size is
336 * just sizeof(struct ifreq)
337 */
338 #ifdef HAVE_SA_LEN
339 #ifndef max
340 #define max(a,b) ((a) > (b) ? (a) : (b))
341 #endif
342 #define ifreq_size(i) max(sizeof(struct ifreq),\
343 sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
344 #else
345 #define ifreq_size(i) sizeof(struct ifreq)
346 #endif /* HAVE_SA_LEN*/
347
348 #ifdef AF_INET6
349 sd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
350 if (sd == INVALID_SOCKET)
351 #endif
352 {
353 sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
354 if (sd == INVALID_SOCKET)
355 {
356 return -1;
357 }
358 }
359 memset(buf, 0, sizeof(buf));
360 ifc.ifc_len = sizeof(buf);
361 ifc.ifc_buf = buf;
362 if (ioctl(sd, SIOCGIFCONF, (char *)&ifc) < 0)
363 {
364 close(sd);
365 return -1;
366 }
367 n = ifc.ifc_len;
368 for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
369 ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
370 strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
371 #if defined(SIOCGIFHWADDR)
372 if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
373 continue;
374 a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
375 #elif defined(SIOCGENADDR)
376 if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
377 continue;
378 a = (unsigned char *) ifr.ifr_enaddr;
379 #else
380 // we don't have a way of getting the hardware address
381 a = (unsigned char *)"\x00\x00\x00\x00\x00\x00";
382 close(sd);
383 return 0;
384 #endif /* SIOCGENADDR / SIOCGIFHWADDR */
385 if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
386 continue;
387 if (node_id)
388 {
389 memcpy(node_id, a, 6);
390 close(sd);
391 return 1;
392 }
393 }
394 close(sd);
395 #endif
396 return 0;
397 }
398
399 /* Assume that the gettimeofday() has microsecond granularity */
400 #define MAX_ADJUSTMENT 10
401
402 static int get_clock(DWORD *clock_high, DWORD *clock_low, WORD *ret_clock_seq)
403 {
404 static int adjustment = 0;
405 static struct timeval last = {0, 0};
406 static unsigned short clock_seq;
407 struct timeval tv;
408 QWORD clock_reg;
409 #ifdef _WIN32
410 FILETIME ft;
411 ULARGE_INTEGER li;
412 unsigned __int64 t;
413 #endif
414
415 try_again:
416 #ifdef _WIN32
417 GetSystemTimeAsFileTime(&ft);
418 li.LowPart = ft.dwLowDateTime;
419 li.HighPart = ft.dwHighDateTime;
420 t = li.QuadPart; // In 100-nanosecond intervals
421 t /= 10; // To microseconds
422 tv.tv_sec = (long)(t / 1000000);
423 tv.tv_usec = (long)(t % 1000000);
424 #else
425 gettimeofday(&tv, NULL);
426 #endif
427 if ((last.tv_sec == 0) && (last.tv_usec == 0))
428 {
429 get_random_bytes(&clock_seq, sizeof(clock_seq));
430 clock_seq &= 0x1FFF;
431 last = tv;
432 last.tv_sec--;
433 }
434 if ((tv.tv_sec < last.tv_sec) ||
435 ((tv.tv_sec == last.tv_sec) &&
436 (tv.tv_usec < last.tv_usec)))
437 {
438 clock_seq = (clock_seq + 1) & 0x1FFF;
439 adjustment = 0;
440 last = tv;
441 }
442 else if ((tv.tv_sec == last.tv_sec) &&
443 (tv.tv_usec == last.tv_usec))
444 {
445 if (adjustment >= MAX_ADJUSTMENT)
446 goto try_again;
447 adjustment++;
448 }
449 else
450 {
451 adjustment = 0;
452 last = tv;
453 }
454
455 clock_reg = tv.tv_usec * 10 + adjustment;
456 clock_reg += ((QWORD)tv.tv_sec) * 10000000;
457 clock_reg += (((QWORD)0x01B21DD2) << 32) + 0x13814000;
458
459 *clock_high = (DWORD)(clock_reg >> 32);
460 *clock_low = (DWORD)clock_reg;
461 *ret_clock_seq = clock_seq;
462 return 0;
463 }
464
465 static void uuid_generate_time(uuid_t out)
466 {
467 static unsigned char node_id[6];
468 static int has_init = 0;
469 struct __uuid uu;
470 DWORD clock_mid;
471
472 if (!has_init)
473 {
474 if (get_node_id(node_id) <= 0)
475 {
476 get_random_bytes(node_id, 6);
477 /*
478 * Set multicast bit, to prevent conflicts
479 * with IEEE 802 addresses obtained from
480 * network cards
481 */
482 node_id[0] |= 0x80;
483 }
484 has_init = 1;
485 }
486 get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
487 uu.clock_seq |= 0x8000;
488 uu.time_mid = (WORD)clock_mid;
489 uu.time_hi_and_version = (WORD)((clock_mid >> 16) | 0x1000);
490 memcpy(uu.node, node_id, 6);
491 uuid_pack(&uu, out);
492 }
493
494 static void uuid_generate_random(uuid_t out)
495 {
496 uuid_t buf;
497 struct __uuid uu;
498
499 get_random_bytes(buf, sizeof(buf));
500 uuid_unpack(buf, &uu);
501
502 uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
503 uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
504 uuid_pack(&uu, out);
505 }
506
507 /*
508 * This is the generic front-end to uuid_generate_random and
509 * uuid_generate_time. It uses uuid_generate_random only if
510 * /dev/urandom is available, since otherwise we won't have
511 * high-quality randomness.
512 */
513 void LIBNETXMS_EXPORTABLE uuid_generate(uuid_t out)
514 {
515 #ifdef _WIN32
516 UUID uuid;
517
518 UuidCreate(&uuid);
519 memcpy(out, &uuid, UUID_LENGTH);
520 #else
521 int fd;
522
523 fd = get_random_fd();
524 if (fd >= 0)
525 {
526 close(fd);
527 uuid_generate_random(out);
528 }
529 else
530 {
531 uuid_generate_time(out);
532 }
533 #endif
534 }