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