]> granicus.if.org Git - postgresql/blob - contrib/uuid-ossp/uuid-ossp.c
Adjust blank lines around PG_MODULE_MAGIC defines, for consistency
[postgresql] / contrib / uuid-ossp / uuid-ossp.c
1 /*-------------------------------------------------------------------------
2  *
3  * UUID generation functions using the BSD, E2FS or OSSP UUID library
4  *
5  * Copyright (c) 2007-2014, PostgreSQL Global Development Group
6  *
7  * Portions Copyright (c) 2009 Andrew Gierth
8  *
9  * contrib/uuid-ossp/uuid-ossp.c
10  *
11  *-------------------------------------------------------------------------
12  */
13
14 #include "postgres.h"
15
16 #include "fmgr.h"
17 #include "utils/builtins.h"
18 #include "utils/uuid.h"
19
20 /*
21  * It's possible that there's more than one uuid.h header file present.
22  * We expect configure to set the HAVE_ symbol for only the one we want.
23  *
24  * BSD includes a uuid_hash() function that conflicts with the one in
25  * builtins.h; we #define it out of the way.
26  */
27 #define uuid_hash bsd_uuid_hash
28
29 #ifdef HAVE_UUID_H
30 #include <uuid.h>
31 #endif
32 #ifdef HAVE_OSSP_UUID_H
33 #include <ossp/uuid.h>
34 #endif
35 #ifdef HAVE_UUID_UUID_H
36 #include <uuid/uuid.h>
37 #endif
38
39 #undef uuid_hash
40
41 /*
42  * Some BSD variants offer md5 and sha1 implementations but Linux does not,
43  * so we use a copy of the ones from pgcrypto.  Not needed with OSSP, though.
44  */
45 #ifndef HAVE_UUID_OSSP
46 #include "md5.h"
47 #include "sha1.h"
48 #endif
49
50
51 /* Check our UUID length against OSSP's; better both be 16 */
52 #if defined(HAVE_UUID_OSSP) && (UUID_LEN != UUID_LEN_BIN)
53 #error UUID length mismatch
54 #endif
55
56 /* Define some constants like OSSP's, to make the code more readable */
57 #ifndef HAVE_UUID_OSSP
58 #define UUID_MAKE_MC 0
59 #define UUID_MAKE_V1 1
60 #define UUID_MAKE_V2 2
61 #define UUID_MAKE_V3 3
62 #define UUID_MAKE_V4 4
63 #define UUID_MAKE_V5 5
64 #endif
65
66 /*
67  * A DCE 1.1 compatible source representation of UUIDs, derived from
68  * the BSD implementation.  BSD already has this; OSSP doesn't need it.
69  */
70 #ifdef HAVE_UUID_E2FS
71 typedef struct
72 {
73         uint32_t        time_low;
74         uint16_t        time_mid;
75         uint16_t        time_hi_and_version;
76         uint8_t         clock_seq_hi_and_reserved;
77         uint8_t         clock_seq_low;
78         uint8_t         node[6];
79 } dce_uuid_t;
80 #else
81 #define dce_uuid_t uuid_t
82 #endif
83
84 /* If not OSSP, we need some endianness-manipulation macros */
85 #ifndef HAVE_UUID_OSSP
86
87 #define UUID_TO_NETWORK(uu) \
88 do { \
89         uu.time_low = htonl(uu.time_low); \
90         uu.time_mid = htons(uu.time_mid); \
91         uu.time_hi_and_version = htons(uu.time_hi_and_version); \
92 } while (0)
93
94 #define UUID_TO_LOCAL(uu) \
95 do { \
96         uu.time_low = ntohl(uu.time_low); \
97         uu.time_mid = ntohs(uu.time_mid); \
98         uu.time_hi_and_version = ntohs(uu.time_hi_and_version); \
99 } while (0)
100
101 #define UUID_V3_OR_V5(uu, v) \
102 do { \
103         uu.time_hi_and_version &= 0x0FFF; \
104         uu.time_hi_and_version |= (v << 12); \
105         uu.clock_seq_hi_and_reserved &= 0x3F; \
106         uu.clock_seq_hi_and_reserved |= 0x80; \
107 } while(0)
108
109 #endif   /* !HAVE_UUID_OSSP */
110
111 PG_MODULE_MAGIC;
112
113 PG_FUNCTION_INFO_V1(uuid_nil);
114 PG_FUNCTION_INFO_V1(uuid_ns_dns);
115 PG_FUNCTION_INFO_V1(uuid_ns_url);
116 PG_FUNCTION_INFO_V1(uuid_ns_oid);
117 PG_FUNCTION_INFO_V1(uuid_ns_x500);
118
119 PG_FUNCTION_INFO_V1(uuid_generate_v1);
120 PG_FUNCTION_INFO_V1(uuid_generate_v1mc);
121 PG_FUNCTION_INFO_V1(uuid_generate_v3);
122 PG_FUNCTION_INFO_V1(uuid_generate_v4);
123 PG_FUNCTION_INFO_V1(uuid_generate_v5);
124
125 #ifdef HAVE_UUID_OSSP
126
127 static void
128 pguuid_complain(uuid_rc_t rc)
129 {
130         char       *err = uuid_error(rc);
131
132         if (err != NULL)
133                 ereport(ERROR,
134                                 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
135                                  errmsg("OSSP uuid library failure: %s", err)));
136         else
137                 ereport(ERROR,
138                                 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
139                                  errmsg("OSSP uuid library failure: error code %d", rc)));
140 }
141
142 /*
143  * We create a uuid_t object just once per session and re-use it for all
144  * operations in this module.  OSSP UUID caches the system MAC address and
145  * other state in this object.  Reusing the object has a number of benefits:
146  * saving the cycles needed to fetch the system MAC address over and over,
147  * reducing the amount of entropy we draw from /dev/urandom, and providing a
148  * positive guarantee that successive generated V1-style UUIDs don't collide.
149  * (On a machine fast enough to generate multiple UUIDs per microsecond,
150  * or whatever the system's wall-clock resolution is, we'd otherwise risk
151  * collisions whenever random initialization of the uuid_t's clock sequence
152  * value chanced to produce duplicates.)
153  *
154  * However: when we're doing V3 or V5 UUID creation, uuid_make needs two
155  * uuid_t objects, one holding the namespace UUID and one for the result.
156  * It's unspecified whether it's safe to use the same uuid_t for both cases,
157  * so let's cache a second uuid_t for use as the namespace holder object.
158  */
159 static uuid_t *
160 get_cached_uuid_t(int which)
161 {
162         static uuid_t *cached_uuid[2] = {NULL, NULL};
163
164         if (cached_uuid[which] == NULL)
165         {
166                 uuid_rc_t       rc;
167
168                 rc = uuid_create(&cached_uuid[which]);
169                 if (rc != UUID_RC_OK)
170                 {
171                         cached_uuid[which] = NULL;
172                         pguuid_complain(rc);
173                 }
174         }
175         return cached_uuid[which];
176 }
177
178 static char *
179 uuid_to_string(const uuid_t *uuid)
180 {
181         char       *buf = palloc(UUID_LEN_STR + 1);
182         void       *ptr = buf;
183         size_t          len = UUID_LEN_STR + 1;
184         uuid_rc_t       rc;
185
186         rc = uuid_export(uuid, UUID_FMT_STR, &ptr, &len);
187         if (rc != UUID_RC_OK)
188                 pguuid_complain(rc);
189
190         return buf;
191 }
192
193
194 static void
195 string_to_uuid(const char *str, uuid_t *uuid)
196 {
197         uuid_rc_t       rc;
198
199         rc = uuid_import(uuid, UUID_FMT_STR, str, UUID_LEN_STR + 1);
200         if (rc != UUID_RC_OK)
201                 pguuid_complain(rc);
202 }
203
204
205 static Datum
206 special_uuid_value(const char *name)
207 {
208         uuid_t     *uuid = get_cached_uuid_t(0);
209         char       *str;
210         uuid_rc_t       rc;
211
212         rc = uuid_load(uuid, name);
213         if (rc != UUID_RC_OK)
214                 pguuid_complain(rc);
215         str = uuid_to_string(uuid);
216
217         return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
218 }
219
220 /* len is unused with OSSP, but we want to have the same number of args */
221 static Datum
222 uuid_generate_internal(int mode, const uuid_t *ns, const char *name, int len)
223 {
224         uuid_t     *uuid = get_cached_uuid_t(0);
225         char       *str;
226         uuid_rc_t       rc;
227
228         rc = uuid_make(uuid, mode, ns, name);
229         if (rc != UUID_RC_OK)
230                 pguuid_complain(rc);
231         str = uuid_to_string(uuid);
232
233         return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
234 }
235
236
237 static Datum
238 uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
239 {
240         uuid_t     *ns_uuid = get_cached_uuid_t(1);
241
242         string_to_uuid(DatumGetCString(DirectFunctionCall1(uuid_out,
243                                                                                                            UUIDPGetDatum(ns))),
244                                    ns_uuid);
245
246         return uuid_generate_internal(mode,
247                                                                   ns_uuid,
248                                                                   text_to_cstring(name),
249                                                                   0);
250 }
251
252 #else                                                   /* !HAVE_UUID_OSSP */
253
254 static Datum
255 uuid_generate_internal(int v, unsigned char *ns, char *ptr, int len)
256 {
257         char            strbuf[40];
258
259         switch (v)
260         {
261                 case 0:                 /* constant-value uuids */
262                         strlcpy(strbuf, ptr, 37);
263                         break;
264
265                 case 1:                 /* time/node-based uuids */
266                         {
267 #ifdef HAVE_UUID_E2FS
268                                 uuid_t          uu;
269
270                                 uuid_generate_time(uu);
271                                 uuid_unparse(uu, strbuf);
272
273                                 /*
274                                  * PTR, if set, replaces the trailing characters of the uuid;
275                                  * this is to support v1mc, where a random multicast MAC is
276                                  * used instead of the physical one
277                                  */
278                                 if (ptr && len <= 36)
279                                         strcpy(strbuf + (36 - len), ptr);
280 #else                                                   /* BSD */
281                                 uuid_t          uu;
282                                 uint32_t        status = uuid_s_ok;
283                                 char       *str = NULL;
284
285                                 uuid_create(&uu, &status);
286
287                                 if (status == uuid_s_ok)
288                                 {
289                                         uuid_to_string(&uu, &str, &status);
290                                         if (status == uuid_s_ok)
291                                         {
292                                                 strlcpy(strbuf, str, 37);
293
294                                                 /*
295                                                  * PTR, if set, replaces the trailing characters of
296                                                  * the uuid; this is to support v1mc, where a random
297                                                  * multicast MAC is used instead of the physical one
298                                                  */
299                                                 if (ptr && len <= 36)
300                                                         strcpy(strbuf + (36 - len), ptr);
301                                         }
302                                         if (str)
303                                                 free(str);
304                                 }
305
306                                 if (status != uuid_s_ok)
307                                         ereport(ERROR,
308                                                         (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
309                                                          errmsg("uuid library failure: %d",
310                                                                         (int) status)));
311 #endif
312                                 break;
313                         }
314
315                 case 3:                 /* namespace-based MD5 uuids */
316                 case 5:                 /* namespace-based SHA1 uuids */
317                         {
318                                 dce_uuid_t      uu;
319 #ifdef HAVE_UUID_BSD
320                                 uint32_t        status = uuid_s_ok;
321                                 char       *str = NULL;
322 #endif
323
324                                 if (v == 3)
325                                 {
326                                         MD5_CTX         ctx;
327
328                                         MD5Init(&ctx);
329                                         MD5Update(&ctx, ns, sizeof(uu));
330                                         MD5Update(&ctx, (unsigned char *) ptr, len);
331                                         /* we assume sizeof MD5 result is 16, same as UUID size */
332                                         MD5Final((unsigned char *) &uu, &ctx);
333                                 }
334                                 else
335                                 {
336                                         SHA1_CTX        ctx;
337                                         unsigned char sha1result[SHA1_RESULTLEN];
338
339                                         SHA1Init(&ctx);
340                                         SHA1Update(&ctx, ns, sizeof(uu));
341                                         SHA1Update(&ctx, (unsigned char *) ptr, len);
342                                         SHA1Final(sha1result, &ctx);
343                                         memcpy(&uu, sha1result, sizeof(uu));
344                                 }
345
346                                 /* the calculated hash is using local order */
347                                 UUID_TO_NETWORK(uu);
348                                 UUID_V3_OR_V5(uu, v);
349
350 #ifdef HAVE_UUID_E2FS
351                                 /* uuid_unparse expects local order */
352                                 UUID_TO_LOCAL(uu);
353                                 uuid_unparse((unsigned char *) &uu, strbuf);
354 #else                                                   /* BSD */
355                                 uuid_to_string(&uu, &str, &status);
356
357                                 if (status == uuid_s_ok)
358                                         strlcpy(strbuf, str, 37);
359
360                                 if (str)
361                                         free(str);
362
363                                 if (status != uuid_s_ok)
364                                         ereport(ERROR,
365                                                         (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
366                                                          errmsg("uuid library failure: %d",
367                                                                         (int) status)));
368 #endif
369                                 break;
370                         }
371
372                 case 4:                 /* random uuid */
373                 default:
374                         {
375 #ifdef HAVE_UUID_E2FS
376                                 uuid_t          uu;
377
378                                 uuid_generate_random(uu);
379                                 uuid_unparse(uu, strbuf);
380 #else                                                   /* BSD */
381                                 snprintf(strbuf, sizeof(strbuf),
382                                                  "%08lx-%04x-%04x-%04x-%04x%08lx",
383                                                  (unsigned long) arc4random(),
384                                                  (unsigned) (arc4random() & 0xffff),
385                                                  (unsigned) ((arc4random() & 0xfff) | 0x4000),
386                                                  (unsigned) ((arc4random() & 0x3fff) | 0x8000),
387                                                  (unsigned) (arc4random() & 0xffff),
388                                                  (unsigned long) arc4random());
389 #endif
390                                 break;
391                         }
392         }
393
394         return DirectFunctionCall1(uuid_in, CStringGetDatum(strbuf));
395 }
396
397 #endif   /* HAVE_UUID_OSSP */
398
399
400 Datum
401 uuid_nil(PG_FUNCTION_ARGS)
402 {
403 #ifdef HAVE_UUID_OSSP
404         return special_uuid_value("nil");
405 #else
406         return uuid_generate_internal(0, NULL,
407                                                                   "00000000-0000-0000-0000-000000000000", 36);
408 #endif
409 }
410
411
412 Datum
413 uuid_ns_dns(PG_FUNCTION_ARGS)
414 {
415 #ifdef HAVE_UUID_OSSP
416         return special_uuid_value("ns:DNS");
417 #else
418         return uuid_generate_internal(0, NULL,
419                                                                   "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
420 #endif
421 }
422
423
424 Datum
425 uuid_ns_url(PG_FUNCTION_ARGS)
426 {
427 #ifdef HAVE_UUID_OSSP
428         return special_uuid_value("ns:URL");
429 #else
430         return uuid_generate_internal(0, NULL,
431                                                                   "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
432 #endif
433 }
434
435
436 Datum
437 uuid_ns_oid(PG_FUNCTION_ARGS)
438 {
439 #ifdef HAVE_UUID_OSSP
440         return special_uuid_value("ns:OID");
441 #else
442         return uuid_generate_internal(0, NULL,
443                                                                   "6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
444 #endif
445 }
446
447
448 Datum
449 uuid_ns_x500(PG_FUNCTION_ARGS)
450 {
451 #ifdef HAVE_UUID_OSSP
452         return special_uuid_value("ns:X500");
453 #else
454         return uuid_generate_internal(0, NULL,
455                                                                   "6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
456 #endif
457 }
458
459
460 Datum
461 uuid_generate_v1(PG_FUNCTION_ARGS)
462 {
463         return uuid_generate_internal(UUID_MAKE_V1, NULL, NULL, 0);
464 }
465
466
467 Datum
468 uuid_generate_v1mc(PG_FUNCTION_ARGS)
469 {
470 #ifdef HAVE_UUID_OSSP
471         char       *buf = NULL;
472 #elif defined(HAVE_UUID_E2FS)
473         char            strbuf[40];
474         char       *buf;
475         uuid_t          uu;
476
477         uuid_generate_random(uu);
478
479         /* set IEEE802 multicast and local-admin bits */
480         ((dce_uuid_t *) &uu)->node[0] |= 0x03;
481
482         uuid_unparse(uu, strbuf);
483         buf = strbuf + 24;
484 #else                                                   /* BSD */
485         char            buf[16];
486
487         /* set IEEE802 multicast and local-admin bits */
488         snprintf(buf, sizeof(buf), "-%04x%08lx",
489                          (unsigned) ((arc4random() & 0xffff) | 0x0300),
490                          (unsigned long) arc4random());
491 #endif
492
493         return uuid_generate_internal(UUID_MAKE_V1 | UUID_MAKE_MC, NULL,
494                                                                   buf, 13);
495 }
496
497
498 Datum
499 uuid_generate_v3(PG_FUNCTION_ARGS)
500 {
501         pg_uuid_t  *ns = PG_GETARG_UUID_P(0);
502         text       *name = PG_GETARG_TEXT_P(1);
503
504 #ifdef HAVE_UUID_OSSP
505         return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name);
506 #else
507         return uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) ns,
508                                                                   VARDATA(name), VARSIZE(name) - VARHDRSZ);
509 #endif
510 }
511
512
513 Datum
514 uuid_generate_v4(PG_FUNCTION_ARGS)
515 {
516         return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL, 0);
517 }
518
519
520 Datum
521 uuid_generate_v5(PG_FUNCTION_ARGS)
522 {
523         pg_uuid_t  *ns = PG_GETARG_UUID_P(0);
524         text       *name = PG_GETARG_TEXT_P(1);
525
526 #ifdef HAVE_UUID_OSSP
527         return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name);
528 #else
529         return uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) ns,
530                                                                   VARDATA(name), VARSIZE(name) - VARHDRSZ);
531 #endif
532 }