xapian-core  2.1.0
uuids.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2008 Lemur Consulting Ltd
5  * Copyright (C) 2013,2015,2016,2017,2018,2026 Olly Betts
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see
19  * <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 
24 #include "uuids.h"
25 
26 #include "xapian/error.h"
27 
28 #include <cerrno>
29 #include <cstring>
30 #include "stringutils.h"
31 
32 #include <sys/types.h>
33 #include "safefcntl.h"
34 #include "safeunistd.h"
35 
36 #ifdef HAVE_ARC4RANDOM_BUF
37 # include <stdlib.h>
38 #endif
39 
40 #ifdef USE_PROC_FOR_UUID
41 # include "safesysstat.h"
42 #elif defined HAVE_UUID_UUID_H
43 # include <exception>
44 # include <uuid/uuid.h>
45 #elif defined HAVE_UUID_H
46 // UUID API on FreeBSD, NetBSD, OpenBSD and AIX.
47 # include <arpa/inet.h> // For htonl() and htons().
48 # include <exception>
49 # include <uuid.h>
50 #elif defined USE_WIN32_UUID_API
51 # include "safewindows.h"
52 # include <rpc.h>
53 # ifdef __WIN32__
54 # include "safewinsock2.h" // For htonl() and htons().
55 # else
56 // Cygwin:
57 # include <arpa/inet.h> // For htonl() and htons().
58 # endif
59 #endif
60 
61 using namespace std;
62 
64 static constexpr unsigned UUID_GAP_MASK = 0x2a8;
65 
66 void
68 {
69  // If the platform provides an API to get cryptographically secure random
70  // data we just fill a buffer and then set/clear the appropriate bits to
71  // turn it into a valid randomly-generated UUID.
72  //
73  // This avoids needing external libraries, using platform-specific APIs
74  // or reading magic files in /proc.
75  //
76  // We could use std::random_device here, except:
77  //
78  // std::random_device may be implemented in terms of an
79  // implementation-defined pseudo-random number engine if a
80  // non-deterministic source (e.g. a hardware device) is not available to
81  // the implementation.
82  //
83  // `std::random_device::entropy()` should allow us to tell but can't be
84  // trusted due to various bad real-world implementations:
85  // https://en.cppreference.com/cpp/numeric/random/random_device/entropy#Notes
86 
87  bool filled_with_randomness = false;
88 #if defined HAVE_ARC4RANDOM_BUF
89  // Apparently available on:
90  // * Android (all API levels)
91  // * DragonFly 1.0
92  // * FreeBSD 8.0
93  // * glibc 2.36
94  // * macOS
95  // * NetBSD 1.6
96  // * OpenBSD 2.1
97  arc4random_buf(uuid_data, BINARY_SIZE);
98  filled_with_randomness = true;
99 # define TRIED_RANDOMNESS
100 #elif defined HAVE_ARC4RANDOM
101  // Apparently available before arc4random_buf() on some platforms, e.g.:
102  // * FreeBSD 3.0
103  static_assert(BINARY_SIZE % 4 == 0, "UUID binary size not multiple of 4");
104  for (unsigned i = 0; i < BINARY_SIZE; i += 4) {
105  uint32_t v = arc4random();
106  memcpy(uuid_data + i, v, 4);
107  }
108  filled_with_randomness = true;
109 # define TRIED_RANDOMNESS
110 #elif defined HAVE_GETENTROPY
111  // Specified by POSIX.1-2024, but has not been supported for as long
112  // as arc4random_buf()/arc4random() on most platforms. A notable exception
113  // is glibc which has supported getentropy() since 2.25.
114  if (getentropy(uuid_data, BINARY_SIZE) == 0) {
115  filled_with_randomness = true;
116  }
117 # define TRIED_RANDOMNESS
118 #endif
119  if (filled_with_randomness) {
120  uuid_data[6] = (uuid_data[6] & 0x0f) | 0x40; // version 4
121  uuid_data[8] = (uuid_data[8] & 0x3f) | 0x80; // RFC 4122
122  return;
123  }
124 
125 #ifdef USE_PROC_FOR_UUID
126  /* Linux (since 2.3.16) has /proc/sys/kernel/random/uuid which generates
127  * a new UUID each time it is read and returns it in string form.
128  *
129  * Some significant downsides of this are that it needs /proc to be
130  * mounted, it requires an unused fd, and access might be blocked by
131  * SELinux or similar (e.g. AOSP SELinux policy only allows access starting
132  * with Android 9).
133  */
134  char buf[STRING_SIZE];
135  int fd = open("/proc/sys/kernel/random/uuid", O_RDONLY);
136  if (rare(fd == -1)) {
137  throw Xapian::DatabaseCreateError("Opening UUID generator failed", errno);
138  }
139  bool failed = (read(fd, buf, STRING_SIZE) != STRING_SIZE);
140  close(fd);
141  if (failed) {
142  throw Xapian::DatabaseCreateError("Generating UUID failed");
143  }
144  parse(buf);
145 #elif defined HAVE_UUID_UUID_H
146  uuid_t uu;
147  uuid_generate(uu);
148  memcpy(uuid_data, &uu, BINARY_SIZE);
149 #elif defined HAVE_UUID_H
150  uuid_t uu;
151  uint32_t status;
152  uuid_create(&uu, &status);
153  if (status != uuid_s_ok) {
154  // Can only be uuid_s_no_memory it seems.
155  throw std::bad_alloc();
156  }
157  uu.time_low = htonl(uu.time_low);
158  uu.time_mid = htons(uu.time_mid);
159  uu.time_hi_and_version = htons(uu.time_hi_and_version);
160  memcpy(uuid_data, &uu, BINARY_SIZE);
161 #elif defined USE_WIN32_UUID_API
162  UUID uuid;
163  if (rare(UuidCreate(&uuid) != RPC_S_OK)) {
164  // Throw a DatabaseCreateError, since we can't make a UUID. The
165  // windows API documentation is a bit unclear about the situations in
166  // which this can happen.
167  throw Xapian::DatabaseCreateError("Cannot create UUID");
168  }
169  uuid.Data1 = htonl(uuid.Data1);
170  uuid.Data2 = htons(uuid.Data2);
171  uuid.Data3 = htons(uuid.Data3);
172  memcpy(uuid_data, &uuid, BINARY_SIZE);
173 #elif defined TRIED_RANDOMNESS
174  throw Xapian::DatabaseCreateError("Generating UUID failed");
175 #else
176 # error Do not know how to generate UUIDs
177 #endif
178 }
179 
180 void
181 Uuid::parse(const char* in)
182 {
183  for (unsigned i = 0; i != BINARY_SIZE; ++i) {
184  uuid_data[i] = hex_decode(in[0], in[1]);
185  in += ((UUID_GAP_MASK >> i) & 1) | 2;
186  }
187 }
188 
189 string
191 {
192  string result;
193  result.reserve(STRING_SIZE);
194  for (unsigned i = 0; i != BINARY_SIZE; ++i) {
195  unsigned char ch = uuid_data[i];
196  result += "0123456789abcdef"[ch >> 4];
197  result += "0123456789abcdef"[ch & 0x0f];
198  if ((UUID_GAP_MASK >> i) & 1)
199  result += '-';
200  }
201  return result;
202 }
void generate()
Definition: uuids.cc:67
std::string to_string() const
Definition: uuids.cc:190
void parse(const char *in)
Definition: uuids.cc:181
DatabaseCreateError indicates a failure to create a database.
Definition: error.h:439
#define rare(COND)
Definition: config.h:616
Hierarchy of classes which Xapian can throw as exceptions.
int close(FD &fd)
Definition: fd.h:63
Database open(std::string_view host, unsigned int port, unsigned timeout=10000, unsigned connect_timeout=10000)
Construct a Database object for read-only access to a remote database accessed via a TCP connection.
include <fcntl.h>, but working around broken platforms.
include <sys/stat.h> with portability enhancements
<unistd.h>, but with compat.
include <windows.h> without all the bloat and damage.
include <winsock2.h> but working around problems.
Various handy string-related helpers.
char hex_decode(char ch1, char ch2)
Decode a pair of ASCII hex digits.
Definition: stringutils.h:248
static constexpr unsigned UUID_GAP_MASK
Bit-mask to determine where to put hyphens in the string representation.
Definition: uuids.cc:64
Class for handling UUIDs.