1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * K.S. Mullender & A. de Rijke
11 * The UUID module
12 * The UUID module contains a wrapper for all function in
13 * libuuid.
14 */
15
16#include "monetdb_config.h"
17#include "mal.h"
18#include "mal_exception.h"
19#include "mal_atom.h" /* for malAtomSize */
20#ifdef HAVE_UUID_UUID_H
21#include <uuid/uuid.h>
22#endif
23#ifndef HAVE_UUID
24#ifdef HAVE_OPENSSL
25# include <openssl/rand.h>
26#else
27#ifdef HAVE_COMMONCRYPTO
28#include <CommonCrypto/CommonRandom.h>
29#endif
30#endif
31#endif
32
33#ifdef HAVE_UUID
34#define UUID_SIZE ((int) sizeof(uuid_t)) /* size of a UUID */
35#else
36#define UUID_SIZE 16 /* size of a UUID */
37#endif
38#define UUID_STRLEN 36 /* length of string representation */
39
40typedef union {
41#ifdef HAVE_HGE
42 hge h; /* force alignment, not otherwise used */
43#else
44 lng l[2]; /* force alignment, not otherwise used */
45#endif
46#ifdef HAVE_UUID
47 uuid_t u;
48#else
49 unsigned char u[UUID_SIZE];
50#endif
51} uuid;
52
53mal_export str UUIDprelude(void *ret);
54mal_export int UUIDcompare(const uuid *l, const uuid *r);
55mal_export ssize_t UUIDfromString(const char *svalue, size_t *len, uuid **retval, bool external);
56mal_export BUN UUIDhash(const void *u);
57mal_export const uuid *UUIDnull(void);
58mal_export uuid *UUIDread(uuid *u, stream *s, size_t cnt);
59mal_export ssize_t UUIDtoString(str *retval, size_t *len, const uuid *value, bool external);
60mal_export gdk_return UUIDwrite(const uuid *u, stream *s, size_t cnt);
61
62mal_export str UUIDgenerateUuid(uuid **retval);
63mal_export str UUIDgenerateUuidInt(uuid **retval, int *d);
64mal_export str UUIDstr2uuid(uuid **retval, str *s);
65mal_export str UUIDuuid2str(str *retval, uuid **u);
66mal_export str UUIDisaUUID(bit *retval, str *u);
67mal_export str UUIDequal(bit *retval, uuid **l, uuid **r);
68
69static uuid uuid_nil; /* automatically initialized as zeros */
70
71str
72UUIDprelude(void *ret)
73{
74 (void) ret;
75 assert(UUID_SIZE == 16);
76 (void) malAtomSize(sizeof(uuid), "uuid");
77 return MAL_SUCCEED;
78}
79
80#define is_uuid_nil(x) (memcmp((x)->u, uuid_nil.u, UUID_SIZE) == 0)
81
82/**
83 * Returns the string representation of the given uuid value.
84 * Warning: GDK function
85 * Returns the length of the string
86 */
87ssize_t
88UUIDtoString(str *retval, size_t *len, const uuid *value, bool external)
89{
90 if (*len <= UUID_STRLEN || *retval == NULL) {
91 if (*retval)
92 GDKfree(*retval);
93 if ((*retval = GDKmalloc(UUID_STRLEN + 1)) == NULL)
94 return -1;
95 *len = UUID_STRLEN + 1;
96 }
97 if (is_uuid_nil(value)) {
98 if (external) {
99 snprintf(*retval, *len, "nil");
100 return 3;
101 }
102 strcpy(*retval, str_nil);
103 return 1;
104 }
105 snprintf(*retval, *len,
106 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
107 value->u[0], value->u[1], value->u[2], value->u[3],
108 value->u[4], value->u[5], value->u[6], value->u[7],
109 value->u[8], value->u[9], value->u[10], value->u[11],
110 value->u[12], value->u[13], value->u[14], value->u[15]);
111 assert(strlen(*retval) == UUID_STRLEN);
112 return UUID_STRLEN;
113}
114
115ssize_t
116UUIDfromString(const char *svalue, size_t *len, uuid **retval, bool external)
117{
118 const char *s = svalue;
119 int i, j;
120
121 if (*len < UUID_SIZE || *retval == NULL) {
122 GDKfree(*retval);
123 if ((*retval = GDKmalloc(UUID_SIZE)) == NULL)
124 return -1;
125 *len = UUID_SIZE;
126 }
127 if (external && strcmp(svalue, "nil") == 0) {
128 **retval = uuid_nil;
129 return 3;
130 }
131 if (GDK_STRNIL(svalue)) {
132 **retval = uuid_nil;
133 return 1;
134 }
135 for (i = 0, j = 0; i < UUID_SIZE; i++) {
136 /* on select locations we allow a '-' in the source string */
137 if (j == 8 || j == 12 || j == 16 || j == 20) {
138 if (*s == '-')
139 s++;
140 }
141 if (isdigit((unsigned char) *s))
142 (*retval)->u[i] = *s - '0';
143 else if ('a' <= *s && *s <= 'f')
144 (*retval)->u[i] = *s - 'a' + 10;
145 else if ('A' <= *s && *s <= 'F')
146 (*retval)->u[i] = *s - 'A' + 10;
147 else
148 goto bailout;
149 s++;
150 j++;
151 (*retval)->u[i] <<= 4;
152 if (isdigit((unsigned char) *s))
153 (*retval)->u[i] |= *s - '0';
154 else if ('a' <= *s && *s <= 'f')
155 (*retval)->u[i] |= *s - 'a' + 10;
156 else if ('A' <= *s && *s <= 'F')
157 (*retval)->u[i] |= *s - 'A' + 10;
158 else
159 goto bailout;
160 s++;
161 j++;
162 }
163 return (ssize_t) (s - svalue);
164
165 bailout:
166 **retval = uuid_nil;
167 return -1;
168}
169
170int
171UUIDcompare(const uuid *l, const uuid *r)
172{
173 return memcmp(l->u, r->u, UUID_SIZE);
174}
175
176str
177UUIDgenerateUuid(uuid **retval)
178{
179 uuid *u;
180 int i = 0, r = 0;
181
182 if (*retval == NULL && (*retval = GDKmalloc(UUID_SIZE)) == NULL)
183 throw(MAL, "uuid.new", SQLSTATE(HY001) MAL_MALLOC_FAIL);
184 u = *retval;
185#ifdef HAVE_UUID
186 uuid_generate(u->u);
187 (void) i;
188 (void) r;
189#else
190#ifdef HAVE_OPENSSL
191 if (RAND_bytes(u->u, 16) < 0)
192#else
193#ifdef HAVE_COMMONCRYPTO
194 if (CCRandomGenerateBytes(u->u, 16) != kCCSuccess)
195#endif
196#endif
197 /* if it failed, use rand */
198 for (i = 0; i < UUID_SIZE;) {
199 r = rand() % 65536;
200 u->u[i++] = (unsigned char) (r >> 8);
201 u->u[i++] = (unsigned char) r;
202 }
203#endif
204 return MAL_SUCCEED;
205}
206
207str
208UUIDgenerateUuidInt(uuid **retval, int *d)
209{
210 (void)d;
211 return UUIDgenerateUuid(retval);
212}
213
214str
215UUIDisaUUID(bit *retval, str *s)
216{
217 uuid u;
218 uuid *pu = &u;
219 size_t l = UUID_SIZE;
220 *retval = UUIDfromString(*s, &l, &pu, false) == UUID_STRLEN;
221 return MAL_SUCCEED;
222}
223
224str
225UUIDstr2uuid(uuid **retval, str *s)
226{
227 size_t l = *retval ? UUID_SIZE : 0;
228
229 if (UUIDfromString(*s, &l, retval, false) == UUID_STRLEN) {
230 return MAL_SUCCEED;
231 }
232 throw(MAL, "uuid.uuid", "Not a UUID");
233}
234
235str
236UUIDuuid2str(str *retval, uuid **u)
237{
238 size_t l = 0;
239 *retval = NULL;
240 if (UUIDtoString(retval, &l, *u, false) < 0)
241 throw(MAL, "uuid.str", GDK_EXCEPTION);
242 return MAL_SUCCEED;
243}
244
245str
246UUIDequal(bit *retval, uuid **l, uuid **r)
247{
248 if (is_uuid_nil(*l) || is_uuid_nil(*r))
249 *retval = bit_nil;
250 else
251 *retval = memcmp((*l)->u, (*r)->u, UUID_SIZE) == 0;
252 return MAL_SUCCEED;
253}
254
255BUN
256UUIDhash(const void *v)
257{
258 const uuid *u = (const uuid *) v;
259 unsigned int u1, u2, u3, u4;
260
261 u1 = (unsigned int) u->u[0] << 24 | (unsigned int) u->u[1] << 16 |
262 (unsigned int) u->u[2] << 8 | (unsigned int) u->u[3];
263 u2 = (unsigned int) u->u[4] << 24 | (unsigned int) u->u[5] << 16 |
264 (unsigned int) u->u[6] << 8 | (unsigned int) u->u[7];
265 u3 = (unsigned int) u->u[8] << 24 | (unsigned int) u->u[9] << 16 |
266 (unsigned int) u->u[10] << 8 | (unsigned int) u->u[11];
267 u4 = (unsigned int) u->u[12] << 24 | (unsigned int) u->u[13] << 16 |
268 (unsigned int) u->u[14] << 8 | (unsigned int) u->u[15];
269 return (BUN) mix_int(u1 ^ u2 ^ u3 ^ u4);
270}
271
272const uuid *
273UUIDnull(void)
274{
275 return &uuid_nil;
276}
277
278uuid *
279UUIDread(uuid *U, stream *s, size_t cnt)
280{
281 uuid *u = U;
282 if (u == NULL && (u = GDKmalloc(cnt * sizeof(uuid))) == NULL)
283 return NULL;
284 if (mnstr_read(s, u, UUID_SIZE, cnt) < (ssize_t) cnt) {
285 if (u != U)
286 GDKfree(u);
287 return NULL;
288 }
289 return u;
290}
291
292gdk_return
293UUIDwrite(const uuid *u, stream *s, size_t cnt)
294{
295 return mnstr_write(s, u, UUID_SIZE, cnt) ? GDK_SUCCEED : GDK_FAIL;
296}
297