write audit log call variant which accepts json objects; old/new value logging for...
[public/netxms.git] / src / libnetxms / uuid.cpp
CommitLineData
296ae03d
VK
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**/
5039dede
AK
23
24#include "libnetxms.h"
296ae03d 25#include <uuid.h>
5039dede
AK
26
27#if HAVE_SYS_IOCTL_H
b947484c 28#include <sys/ioctl.h>
5039dede 29#endif
b947484c
VK
30
31#if HAVE_SYS_SOCKIO_H
5039dede
AK
32#include <sys/sockio.h>
33#endif
b947484c
VK
34
35#if HAVE_NET_IF_H
5039dede
AK
36#include <net/if.h>
37#endif
b947484c
VK
38
39#if HAVE_NET_IF_ARP_H
5039dede
AK
40#include <net/if_arp.h>
41#endif
b947484c
VK
42
43#if HAVE_NET_IF_DL_H
5039dede
AK
44#include <net/if_dl.h>
45#endif
46
b947484c
VK
47#if HAVE_SYS_UTSNAME_H
48#include <sys/utsname.h>
49#endif
50
5039dede 51#ifdef HAVE_SRANDOM
296ae03d
VK
52#define srand(x) srandom(x)
53#define rand() random()
5039dede
AK
54#endif
55
de4af576
VK
56/**
57 * NULL GUID
58 */
59const uuid uuid::NULL_UUID = uuid();
60
296ae03d
VK
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 */
70struct __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 */
82static 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 */
117static 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 */
999945fa 146void LIBNETXMS_EXPORTABLE _uuid_clear(uuid_t uu)
296ae03d
VK
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 */
999945fa 158int LIBNETXMS_EXPORTABLE _uuid_compare(const uuid_t uu1, const uuid_t uu2)
296ae03d
VK
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
de4af576 174 * Returns true if the uuid is the NULL uuid
296ae03d 175 */
999945fa 176bool LIBNETXMS_EXPORTABLE _uuid_is_null(const uuid_t uu)
296ae03d
VK
177{
178 const unsigned char *cp;
179 int i;
180
181 for (i=0, cp = uu; i < 16; i++)
182 if (*cp++)
de4af576
VK
183 return false;
184 return true;
296ae03d
VK
185}
186
187/**
188 * Parse UUID
189 */
999945fa 190int LIBNETXMS_EXPORTABLE _uuid_parse(const TCHAR *in, uuid_t uu)
296ae03d
VK
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 */
999945fa 230TCHAR LIBNETXMS_EXPORTABLE *_uuid_to_string(const uuid_t uu, TCHAR *out)
296ae03d
VK
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
0d402aa8
VK
244#ifdef UNICODE
245
246/**
247 * Convert packed UUID to string (non-UNICODE version)
248 */
249char LIBNETXMS_EXPORTABLE *_uuid_to_stringA(const uuid_t uu, char *out)
250{
251 struct __uuid uuid;
252
253 uuid_unpack(uu, &uuid);
254 snprintf(out, 64,
255 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
256 uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
257 uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
258 uuid.node[0], uuid.node[1], uuid.node[2],
259 uuid.node[3], uuid.node[4], uuid.node[5]);
260 return out;
261}
262
263#endif
264
5039dede
AK
265#ifndef _WIN32
266
267static int get_random_fd()
268{
269 int fd = -2;
270 int i;
271
272 if (fd == -2)
273 {
274 fd = open("/dev/urandom", O_RDONLY);
275 if (fd == -1)
276 fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
277 srand((getpid() << 16) ^ getuid() ^ time(0));
278 }
279 /* Crank the random number generator a few times */
280 for (i = time(0) & 0x1F; i > 0; i--)
281 rand();
282 return fd;
283}
284
285#endif
286
5039dede
AK
287/*
288 * Generate a series of random bytes. Use /dev/urandom if possible,
289 * and if not, use srandom/random.
290 */
5039dede
AK
291static void get_random_bytes(void *buf, int nbytes)
292{
293 int i;
294 char *cp = (char *)buf;
295#ifndef _WIN32
296 int fd = get_random_fd();
297 int lose_counter = 0;
298
299 if (fd >= 0)
300 {
301 while (nbytes > 0)
302 {
303 i = read(fd, cp, nbytes);
304 if ((i < 0) &&
305 ((errno == EINTR) || (errno == EAGAIN)))
306 continue;
307 if (i <= 0) {
308 if (lose_counter++ == 8)
309 break;
310 continue;
311 }
312 nbytes -= i;
313 cp += i;
314 lose_counter = 0;
315 }
316 close(fd);
317 }
318#endif
319 if (nbytes == 0)
320 return;
321
322 /* FIXME: put something better here if no /dev/random! */
ee088bd0 323 srand((unsigned int)time(NULL) ^ GetCurrentProcessId());
5039dede
AK
324 for (i = 0; i < nbytes; i++)
325 *cp++ = rand() & 0xFF;
326 return;
327}
328
329/*
52db3ed1 330 * Get the ethernet hardware address or other available node ID
5039dede
AK
331 */
332static int get_node_id(unsigned char *node_id)
333{
52db3ed1
VK
334#if defined(_AIX)
335
336 struct utsname info;
337 if (uname(&info) == -1)
338 return -1;
339
340 memset(node_id, 0, 6);
341 StrToBinA(info.machine, node_id, 6);
342 return 0;
343
344#elif defined(HAVE_NET_IF_H) && !defined(sun) && !defined(__sun) && (defined(SIOCGIFHWADDR) || defined(SIOCGENADDR))
345
5039dede
AK
346 int sd;
347 struct ifreq ifr, *ifrp;
348 struct ifconf ifc;
349 char buf[1024];
350 int n, i;
351 unsigned char *a;
352
353/*
354 * BSD 4.4 defines the size of an ifreq to be
355 * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
356 * However, under earlier systems, sa_len isn't present, so the size is
357 * just sizeof(struct ifreq)
358 */
359#ifdef HAVE_SA_LEN
360#ifndef max
361#define max(a,b) ((a) > (b) ? (a) : (b))
362#endif
363#define ifreq_size(i) max(sizeof(struct ifreq),\
364 sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
365#else
366#define ifreq_size(i) sizeof(struct ifreq)
367#endif /* HAVE_SA_LEN*/
368
369#ifdef AF_INET6
370 sd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
4c0c75c7 371 if (sd == INVALID_SOCKET)
5039dede
AK
372#endif
373 {
374 sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
4c0c75c7 375 if (sd == INVALID_SOCKET)
5039dede
AK
376 {
377 return -1;
378 }
379 }
380 memset(buf, 0, sizeof(buf));
381 ifc.ifc_len = sizeof(buf);
382 ifc.ifc_buf = buf;
383 if (ioctl(sd, SIOCGIFCONF, (char *)&ifc) < 0)
384 {
385 close(sd);
386 return -1;
387 }
388 n = ifc.ifc_len;
389 for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
390 ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
391 strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
52db3ed1 392#if defined(SIOCGIFHWADDR)
5039dede
AK
393 if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
394 continue;
395 a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
52db3ed1 396#elif defined(SIOCGENADDR)
5039dede
AK
397 if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
398 continue;
399 a = (unsigned char *) ifr.ifr_enaddr;
400#else
52db3ed1 401 // we don't have a way of getting the hardware address
5039dede
AK
402 a = (unsigned char *)"\x00\x00\x00\x00\x00\x00";
403 close(sd);
404 return 0;
52db3ed1 405#endif /* SIOCGENADDR / SIOCGIFHWADDR */
5039dede
AK
406 if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
407 continue;
408 if (node_id)
409 {
410 memcpy(node_id, a, 6);
411 close(sd);
412 return 1;
413 }
414 }
415 close(sd);
416#endif
417 return 0;
418}
419
420/* Assume that the gettimeofday() has microsecond granularity */
421#define MAX_ADJUSTMENT 10
422
423static int get_clock(DWORD *clock_high, DWORD *clock_low, WORD *ret_clock_seq)
424{
425 static int adjustment = 0;
426 static struct timeval last = {0, 0};
427 static unsigned short clock_seq;
428 struct timeval tv;
429 QWORD clock_reg;
430#ifdef _WIN32
431 FILETIME ft;
432 ULARGE_INTEGER li;
433 unsigned __int64 t;
434#endif
435
436try_again:
437#ifdef _WIN32
438 GetSystemTimeAsFileTime(&ft);
439 li.LowPart = ft.dwLowDateTime;
440 li.HighPart = ft.dwHighDateTime;
441 t = li.QuadPart; // In 100-nanosecond intervals
442 t /= 10; // To microseconds
443 tv.tv_sec = (long)(t / 1000000);
444 tv.tv_usec = (long)(t % 1000000);
445#else
446 gettimeofday(&tv, NULL);
447#endif
448 if ((last.tv_sec == 0) && (last.tv_usec == 0))
449 {
450 get_random_bytes(&clock_seq, sizeof(clock_seq));
451 clock_seq &= 0x1FFF;
452 last = tv;
453 last.tv_sec--;
454 }
455 if ((tv.tv_sec < last.tv_sec) ||
456 ((tv.tv_sec == last.tv_sec) &&
457 (tv.tv_usec < last.tv_usec)))
458 {
459 clock_seq = (clock_seq + 1) & 0x1FFF;
460 adjustment = 0;
461 last = tv;
462 }
463 else if ((tv.tv_sec == last.tv_sec) &&
464 (tv.tv_usec == last.tv_usec))
465 {
466 if (adjustment >= MAX_ADJUSTMENT)
467 goto try_again;
468 adjustment++;
469 }
470 else
471 {
472 adjustment = 0;
473 last = tv;
474 }
475
476 clock_reg = tv.tv_usec * 10 + adjustment;
477 clock_reg += ((QWORD)tv.tv_sec) * 10000000;
478 clock_reg += (((QWORD)0x01B21DD2) << 32) + 0x13814000;
479
480 *clock_high = (DWORD)(clock_reg >> 32);
481 *clock_low = (DWORD)clock_reg;
482 *ret_clock_seq = clock_seq;
483 return 0;
484}
485
486static void uuid_generate_time(uuid_t out)
487{
488 static unsigned char node_id[6];
489 static int has_init = 0;
296ae03d 490 struct __uuid uu;
5039dede
AK
491 DWORD clock_mid;
492
493 if (!has_init)
494 {
495 if (get_node_id(node_id) <= 0)
496 {
497 get_random_bytes(node_id, 6);
498 /*
499 * Set multicast bit, to prevent conflicts
500 * with IEEE 802 addresses obtained from
501 * network cards
502 */
503 node_id[0] |= 0x80;
504 }
505 has_init = 1;
506 }
507 get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
508 uu.clock_seq |= 0x8000;
509 uu.time_mid = (WORD)clock_mid;
510 uu.time_hi_and_version = (WORD)((clock_mid >> 16) | 0x1000);
511 memcpy(uu.node, node_id, 6);
512 uuid_pack(&uu, out);
513}
514
515static void uuid_generate_random(uuid_t out)
516{
517 uuid_t buf;
296ae03d 518 struct __uuid uu;
5039dede
AK
519
520 get_random_bytes(buf, sizeof(buf));
521 uuid_unpack(buf, &uu);
522
523 uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
524 uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
525 uuid_pack(&uu, out);
526}
527
528/*
529 * This is the generic front-end to uuid_generate_random and
530 * uuid_generate_time. It uses uuid_generate_random only if
531 * /dev/urandom is available, since otherwise we won't have
532 * high-quality randomness.
533 */
999945fa 534void LIBNETXMS_EXPORTABLE _uuid_generate(uuid_t out)
5039dede 535{
3f7c0fe4
VK
536#ifdef _WIN32
537 UUID uuid;
538
539 UuidCreate(&uuid);
540 memcpy(out, &uuid, UUID_LENGTH);
541#else
5039dede
AK
542 int fd;
543
544 fd = get_random_fd();
545 if (fd >= 0)
546 {
547 close(fd);
548 uuid_generate_random(out);
549 }
550 else
3f7c0fe4 551 {
5039dede 552 uuid_generate_time(out);
3f7c0fe4
VK
553 }
554#endif
5039dede 555}