1 /*
2 Copyright (c) DataStax, Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #include "uuids.hpp"
18
19 #include "cassandra.h"
20 #include "external.hpp"
21 #include "get_time.hpp"
22 #include "logger.hpp"
23 #include "md5.hpp"
24 #include "scoped_lock.hpp"
25 #include "serialization.hpp"
26
27 #include <ctype.h>
28 #include <stdio.h>
29
30 #define TIME_OFFSET_BETWEEN_UTC_AND_EPOCH 0x01B21DD213814000LL // Nanoseconds
31 #define MIN_CLOCK_SEQ_AND_NODE 0x8080808080808080LL
32 #define MAX_CLOCK_SEQ_AND_NODE 0x7f7f7f7f7f7f7f7fLL
33
34 using namespace datastax::internal;
35 using namespace datastax::internal::core;
36
to_milliseconds(uint64_t timestamp)37 static uint64_t to_milliseconds(uint64_t timestamp) { return timestamp / 10000L; }
38
from_unix_timestamp(uint64_t timestamp)39 static uint64_t from_unix_timestamp(uint64_t timestamp) {
40 return (timestamp * 10000L) + TIME_OFFSET_BETWEEN_UTC_AND_EPOCH;
41 }
42
set_version(uint64_t timestamp,uint8_t version)43 static uint64_t set_version(uint64_t timestamp, uint8_t version) {
44 return (timestamp & 0x0FFFFFFFFFFFFFFFLL) | (static_cast<uint64_t>(version) << 60);
45 }
46
47 extern "C" {
48
cass_uuid_gen_new()49 CassUuidGen* cass_uuid_gen_new() { return CassUuidGen::to(new UuidGen()); }
50
cass_uuid_gen_new_with_node(cass_uint64_t node)51 CassUuidGen* cass_uuid_gen_new_with_node(cass_uint64_t node) {
52 return CassUuidGen::to(new UuidGen(node));
53 }
54
cass_uuid_gen_free(CassUuidGen * uuid_gen)55 void cass_uuid_gen_free(CassUuidGen* uuid_gen) { delete uuid_gen->from(); }
56
cass_uuid_gen_time(CassUuidGen * uuid_gen,CassUuid * output)57 void cass_uuid_gen_time(CassUuidGen* uuid_gen, CassUuid* output) {
58 uuid_gen->generate_time(output);
59 }
60
cass_uuid_gen_random(CassUuidGen * uuid_gen,CassUuid * output)61 void cass_uuid_gen_random(CassUuidGen* uuid_gen, CassUuid* output) {
62 uuid_gen->generate_random(output);
63 }
64
cass_uuid_gen_from_time(CassUuidGen * uuid_gen,cass_uint64_t timestamp,CassUuid * output)65 void cass_uuid_gen_from_time(CassUuidGen* uuid_gen, cass_uint64_t timestamp, CassUuid* output) {
66 uuid_gen->from_time(timestamp, output);
67 }
68
cass_uuid_min_from_time(cass_uint64_t timestamp,CassUuid * output)69 void cass_uuid_min_from_time(cass_uint64_t timestamp, CassUuid* output) {
70 output->time_and_version = set_version(from_unix_timestamp(timestamp), 1);
71 output->clock_seq_and_node = MIN_CLOCK_SEQ_AND_NODE;
72 }
73
cass_uuid_max_from_time(cass_uint64_t timestamp,CassUuid * output)74 void cass_uuid_max_from_time(cass_uint64_t timestamp, CassUuid* output) {
75 output->time_and_version = set_version(from_unix_timestamp(timestamp), 1);
76 output->clock_seq_and_node = MAX_CLOCK_SEQ_AND_NODE;
77 }
78
cass_uuid_timestamp(CassUuid uuid)79 cass_uint64_t cass_uuid_timestamp(CassUuid uuid) {
80 uint64_t timestamp = uuid.time_and_version & 0x0FFFFFFFFFFFFFFFLL; // Clear version
81 return to_milliseconds(timestamp - TIME_OFFSET_BETWEEN_UTC_AND_EPOCH);
82 }
83
cass_uuid_version(CassUuid uuid)84 cass_uint8_t cass_uuid_version(CassUuid uuid) { return (uuid.time_and_version >> 60) & 0x0F; }
85
cass_uuid_string(CassUuid uuid,char * output)86 void cass_uuid_string(CassUuid uuid, char* output) {
87 size_t pos = 0;
88 char encoded[16];
89 encode_uuid(encoded, uuid);
90 static const char half_byte_to_hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
91 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
92 for (size_t i = 0; i < 16; ++i) {
93 if (i == 4 || i == 6 || i == 8 || i == 10) {
94 output[pos++] = '-';
95 }
96 uint8_t byte = static_cast<uint8_t>(encoded[i]);
97 output[pos++] = half_byte_to_hex[(byte >> 4) & 0x0F];
98 output[pos++] = half_byte_to_hex[byte & 0x0F];
99 }
100 output[pos] = '\0';
101 }
102
cass_uuid_from_string(const char * str,CassUuid * output)103 CassError cass_uuid_from_string(const char* str, CassUuid* output) {
104 if (str == NULL) {
105 return CASS_ERROR_LIB_BAD_PARAMS;
106 }
107
108 return cass_uuid_from_string_n(str, strlen(str), output);
109 }
110
cass_uuid_from_string_n(const char * str,size_t str_length,CassUuid * output)111 CassError cass_uuid_from_string_n(const char* str, size_t str_length, CassUuid* output) {
112 const char* pos = str;
113 char buf[16];
114
115 // clang-format off
116 static const signed char hex_to_half_byte[256] = {
117 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
118 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
119 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
121 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
122 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
123 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
124 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
125 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
126 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
127 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
128 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
129 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
130 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
131 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
132 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
133 };
134 // clang-format on
135
136 if (str == NULL || str_length != 36) {
137 return CASS_ERROR_LIB_BAD_PARAMS;
138 }
139
140 const char* end = str + 36;
141 for (size_t i = 0; i < 16; ++i) {
142 if (pos < end && *pos == '-') pos++;
143 if (pos + 2 > end) {
144 return CASS_ERROR_LIB_BAD_PARAMS;
145 }
146 uint8_t p0 = static_cast<uint8_t>(pos[0]);
147 uint8_t p1 = static_cast<uint8_t>(pos[1]);
148 if (hex_to_half_byte[p0] == -1 || hex_to_half_byte[p1] == -1) {
149 return CASS_ERROR_LIB_BAD_PARAMS;
150 }
151 buf[i] = static_cast<char>(hex_to_half_byte[p0] << 4) + hex_to_half_byte[p1];
152 pos += 2;
153 }
154
155 decode_uuid(buf, output);
156
157 return CASS_OK;
158 }
159
160 } // extern "C"
161
UuidGen()162 UuidGen::UuidGen()
163 : clock_seq_and_node_(0)
164 , last_timestamp_(0LL)
165 , ng_(get_random_seed(MT19937_64::DEFAULT_SEED)) {
166 uv_mutex_init(&mutex_);
167
168 Md5 md5;
169 bool has_unique = false;
170 uv_interface_address_t* addresses;
171 int address_count;
172
173 if (uv_interface_addresses(&addresses, &address_count) == 0) {
174 for (int i = 0; i < address_count; ++i) {
175 char buf[256];
176 uv_interface_address_t address = addresses[i];
177 md5.update(reinterpret_cast<const uint8_t*>(address.name), strlen(address.name));
178 if (address.address.address4.sin_family == AF_INET) {
179 uv_ip4_name(&address.address.address4, buf, sizeof(buf));
180 md5.update(reinterpret_cast<const uint8_t*>(buf), strlen(buf));
181 has_unique = true;
182 } else if (address.address.address4.sin_family == AF_INET6) {
183 uv_ip6_name(&address.address.address6, buf, sizeof(buf));
184 md5.update(reinterpret_cast<const uint8_t*>(buf), strlen(buf));
185 has_unique = true;
186 }
187 }
188 uv_free_interface_addresses(addresses, address_count);
189 }
190
191 uint64_t node = 0;
192 if (has_unique) {
193 uv_cpu_info_t* cpu_infos;
194 int cpu_count;
195 if (uv_cpu_info(&cpu_infos, &cpu_count) == 0) {
196 for (int i = 0; i < cpu_count; ++i) {
197 uv_cpu_info_t cpu_info = cpu_infos[i];
198 md5.update(reinterpret_cast<const uint8_t*>(cpu_info.model), strlen(cpu_info.model));
199 }
200 uv_free_cpu_info(cpu_infos, cpu_count);
201 }
202
203 // Tack on the pid
204 int32_t pid = get_pid();
205 md5.update(reinterpret_cast<const uint8_t*>(&pid), 4);
206
207 uint8_t hash[16];
208 md5.final(hash);
209
210 for (int i = 0; i < 6; ++i) {
211 node |= (0x00000000000000FFLL & (long)hash[i]) << (i * 8);
212 }
213 } else {
214 LOG_INFO("Unable to determine unique data for this node. Generating a random node value.");
215 node = ng_() & 0x0000FFFFFFFFFFFFLL;
216 }
217
218 node |= 0x0000010000000000LL; // Multicast bit
219
220 set_clock_seq_and_node(node);
221 }
222
UuidGen(uint64_t node)223 UuidGen::UuidGen(uint64_t node)
224 : clock_seq_and_node_(0)
225 , last_timestamp_(0LL)
226 , ng_(get_random_seed(MT19937_64::DEFAULT_SEED)) {
227 uv_mutex_init(&mutex_);
228 set_clock_seq_and_node(node & 0x0000FFFFFFFFFFFFLL);
229 }
230
~UuidGen()231 UuidGen::~UuidGen() { uv_mutex_destroy(&mutex_); }
232
generate_time(CassUuid * output)233 void UuidGen::generate_time(CassUuid* output) {
234 output->time_and_version = set_version(monotonic_timestamp(), 1);
235 output->clock_seq_and_node = clock_seq_and_node_;
236 }
237
from_time(uint64_t timestamp,CassUuid * output)238 void UuidGen::from_time(uint64_t timestamp, CassUuid* output) {
239 output->time_and_version = set_version(from_unix_timestamp(timestamp), 1);
240 output->clock_seq_and_node = clock_seq_and_node_;
241 }
242
generate_random(CassUuid * output)243 void UuidGen::generate_random(CassUuid* output) {
244 ScopedMutex lock(&mutex_);
245 uint64_t time_and_version = ng_();
246 uint64_t clock_seq_and_node = ng_();
247 lock.unlock();
248
249 output->time_and_version = set_version(time_and_version, 4);
250 output->clock_seq_and_node =
251 (clock_seq_and_node & 0x3FFFFFFFFFFFFFFFLL) | 0x8000000000000000LL; // RFC4122 variant
252 }
253
set_clock_seq_and_node(uint64_t node)254 void UuidGen::set_clock_seq_and_node(uint64_t node) {
255 uint64_t clock_seq = ng_();
256 clock_seq_and_node_ |= (clock_seq & 0x0000000000003FFFLL) << 48;
257 clock_seq_and_node_ |= 0x8000000000000000LL; // RFC4122 variant
258 clock_seq_and_node_ |= node;
259 }
260
monotonic_timestamp()261 uint64_t UuidGen::monotonic_timestamp() {
262 while (true) {
263 uint64_t now = from_unix_timestamp(get_time_since_epoch_ms());
264 uint64_t last = last_timestamp_.load();
265 if (now > last) {
266 if (last_timestamp_.compare_exchange_strong(last, now)) {
267 return now;
268 }
269 } else {
270 uint64_t last_ms = to_milliseconds(last);
271 if (to_milliseconds(now) < last_ms) {
272 return last_timestamp_.fetch_add(1);
273 }
274 uint64_t candidate = last + 1;
275 if (to_milliseconds(candidate) == last_ms &&
276 last_timestamp_.compare_exchange_strong(last, candidate)) {
277 return candidate;
278 }
279 }
280 }
281 }
282