C++ Distributed Hash Table
infohash.h
1 /*
2  * Copyright (C) 2014-2022 Savoir-faire Linux Inc.
3  * Author : Adrien BĂ©raud <adrien.beraud@savoirfairelinux.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
19 #pragma once
20 
21 #include "def.h"
22 #include "rng.h"
23 
24 #include <msgpack.hpp>
25 
26 #ifndef _WIN32
27 #include <netinet/in.h>
28 #include <netdb.h>
29 #ifdef __ANDROID__
30 typedef uint16_t in_port_t;
31 #endif
32 #else
33 #include <iso646.h>
34 #include <ws2tcpip.h>
35 typedef uint16_t sa_family_t;
36 typedef uint16_t in_port_t;
37 #endif
38 
39 #include <iostream>
40 #include <iomanip>
41 #include <array>
42 #include <vector>
43 #include <string_view>
44 #include <algorithm>
45 #include <stdexcept>
46 #include <sstream>
47 #include <cstring>
48 #include <cstddef>
49 
50 namespace dht {
51 
52 using byte = uint8_t;
53 
54 namespace crypto {
55  OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
56 }
57 
63 template <size_t N>
64 class OPENDHT_PUBLIC Hash {
65 public:
66  using T = std::array<uint8_t, N>;
67  typedef typename T::iterator iterator;
68  typedef typename T::const_iterator const_iterator;
69 
70  Hash() noexcept {
71  data_.fill(0);
72  }
73  Hash(const uint8_t* h, size_t data_len) {
74  if (data_len < N)
75  data_.fill(0);
76  else
77  std::copy_n(h, N, data_.begin());
78  }
84  explicit Hash(std::string_view hex) {
85  if (hex.size() < 2*N)
86  data_.fill(0);
87  else
88  fromString(hex.data());
89  }
90 
91  Hash(const msgpack::object& o) {
92  msgpack_unpack(o);
93  }
94 
95  static constexpr size_t size() noexcept { return N; }
96  const uint8_t* data() const { return data_.data(); }
97  uint8_t* data() { return data_.data(); }
98  iterator begin() { return data_.begin(); }
99  const_iterator cbegin() const { return data_.cbegin(); }
100  iterator end() { return data_.end(); }
101  const_iterator cend() const { return data_.cend(); }
102 
103  static constexpr inline Hash zero() noexcept { return Hash{}; }
104 
105  bool operator==(const Hash& h) const {
106  auto a = reinterpret_cast<const uint32_t*>(data_.data());
107  auto b = reinterpret_cast<const uint32_t*>(h.data_.data());
108  constexpr unsigned n = N / sizeof(uint32_t);
109  for (unsigned i=0; i < n; i++)
110  if (a[i] != b[i])
111  return false;
112  return true;
113  }
114  bool operator!=(const Hash& h) const { return !(*this == h); }
115 
116  bool operator<(const Hash& o) const {
117  for(unsigned i = 0; i < N; i++) {
118  if(data_[i] != o.data_[i])
119  return data_[i] < o.data_[i];
120  }
121  return false;
122  }
123 
124  Hash operator^(const Hash& o) const {
125  Hash result;
126  for(auto i = 0u; i < N; i++) {
127  result[i] = data_[i] ^ o.data_[i];
128  }
129  return result;
130  }
131 
132  explicit operator bool() const {
133  auto a = reinterpret_cast<const uint32_t*>(data_.data());
134  auto b = reinterpret_cast<const uint32_t*>(data_.data() + N);
135  for (; a != b; a++) {
136  if (*a)
137  return true;
138  }
139  return false;
140  }
141 
142  uint8_t& operator[](size_t index) { return data_[index]; }
143  const uint8_t& operator[](size_t index) const { return data_[index]; }
144 
149  inline int lowbit() const {
150  int i, j;
151  for(i = N-1; i >= 0; i--)
152  if(data_[i] != 0)
153  break;
154  if(i < 0)
155  return -1;
156  for(j = 7; j >= 0; j--)
157  if((data_[i] & (0x80 >> j)) != 0)
158  break;
159  return 8 * i + j;
160  }
161 
162  static inline int cmp(const Hash& id1, const Hash& id2) {
163  return std::memcmp(id1.data_.data(), id2.data_.data(), N);
164  }
165 
167  static inline unsigned
168  commonBits(const Hash& id1, const Hash& id2)
169  {
170  unsigned i, j;
171  uint8_t x;
172  for(i = 0; i < N; i++) {
173  if(id1.data_[i] != id2.data_[i])
174  break;
175  }
176 
177  if(i == N)
178  return 8*N;
179 
180  x = id1.data_[i] ^ id2.data_[i];
181 
182  j = 0;
183  while((x & 0x80) == 0) {
184  x <<= 1;
185  j++;
186  }
187 
188  return 8 * i + j;
189  }
190 
192  int
193  xorCmp(const Hash& id1, const Hash& id2) const
194  {
195  for (unsigned i = 0; i < N; i++) {
196  if(id1.data_[i] == id2.data_[i])
197  continue;
198  uint8_t xor1 = id1.data_[i] ^ data_[i];
199  uint8_t xor2 = id2.data_[i] ^ data_[i];
200  return (xor1 < xor2) ? -1 : 1;
201  }
202  return 0;
203  }
204 
205  bool
206  getBit(unsigned nbit) const
207  {
208  auto& num = *(data_.cbegin()+(nbit/8));
209  unsigned bit = 7 - (nbit % 8);
210  return (num >> bit) & 1;
211  }
212 
213  void
214  setBit(unsigned nbit, bool b)
215  {
216  auto& num = data_[nbit/8];
217  unsigned bit = 7 - (nbit % 8);
218  num ^= (-b ^ num) & (1 << bit);
219  }
220 
221  double toFloat() const {
222  using D = size_t;
223  double v = 0.;
224  for (size_t i = 0; i < std::min<size_t>(N, sizeof(D)-1); i++)
225  v += *(data_.cbegin()+i) / (double)((D)1 << 8*(i+1));
226  return v;
227  }
228 
229  static inline Hash get(std::string_view data) {
230  return get((const uint8_t*)data.data(), data.size());
231  }
232 
233  static inline Hash get(const std::vector<uint8_t>& data) {
234  return get(data.data(), data.size());
235  }
236 
240  static Hash get(const uint8_t* data, size_t data_len)
241  {
242  Hash ret;
243  crypto::hash(data, data_len, ret.data(), N);
244  return ret;
245  }
246 
247  static Hash getRandom();
248 
249  template <typename Rd>
250  static Hash getRandom(Rd&);
251 
252  template <size_t M>
253  OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash<M>& h);
254 
255  template <size_t M>
256  OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash<M>& h);
257 
258  const char* to_c_str() const;
259 
260  std::string toString() const;
261 
262  template <typename Packer>
263  void msgpack_pack(Packer& pk) const
264  {
265  pk.pack_bin(N);
266  pk.pack_bin_body((char*)data_.data(), N);
267  }
268 
269  void msgpack_unpack(msgpack::object o) {
270  if (o.type != msgpack::type::BIN or o.via.bin.size != N)
271  throw msgpack::type_error();
272  std::copy_n(o.via.bin.ptr, N, data_.data());
273  }
274 private:
275  T data_;
276  void fromString(const char*);
277 };
278 
279 #define HASH_LEN 20u
280 using InfoHash = Hash<HASH_LEN>;
281 using h256 = Hash<32>;
282 using PkId = h256;
283 
284 template <size_t N>
285 std::ostream& operator<< (std::ostream& s, const Hash<N>& h)
286 {
287  s.write(h.to_c_str(), N*2);
288  return s;
289 }
290 
291 template <size_t N>
292 std::istream& operator>> (std::istream& s, Hash<N>& h)
293 {
294  std::array<char, h.size()*2> dat;
295  s.exceptions(std::istream::eofbit | std::istream::failbit);
296  s.read(&(*dat.begin()), dat.size());
297  fromString(dat.data());
298  return s;
299 }
300 
301 template <size_t N>
302 void
303 Hash<N>::fromString(const char* in) {
304  auto hex2bin = [](char c) -> uint8_t {
305  if (c >= 'a' and c <= 'f') return 10 + c - 'a';
306  else if (c >= 'A' and c <= 'F') return 10 + c - 'A';
307  else if (c >= '0' and c <= '9') return c - '0';
308  else throw std::domain_error("not an hex character");
309  };
310  try {
311  for (size_t i=0; i<N; i++)
312  data_[i] = (hex2bin(in[2*i]) << 4) | hex2bin(in[2*i+1]);
313  } catch (const std::domain_error&) {
314  data_.fill(0);
315  }
316 }
317 
318 template <size_t N>
319 Hash<N>
320 Hash<N>::getRandom()
321 {
322  Hash h;
323  crypto::random_device rdev;
324  std::uniform_int_distribution<uint32_t> rand_int;
325  auto a = reinterpret_cast<uint32_t*>(h.data());
326  auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
327  std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
328  return h;
329 }
330 
331 template <size_t N>
332 template <typename Rd>
333 Hash<N>
334 Hash<N>::getRandom(Rd& rdev)
335 {
336  Hash h;
337  std::uniform_int_distribution<uint32_t> rand_int;
338  auto a = reinterpret_cast<uint32_t*>(h.data());
339  auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
340  std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
341  return h;
342 }
343 
344 struct alignas(std::max_align_t) HexMap : public std::array<std::array<char, 2>, 256> {
345  HexMap() {
346  for (size_t i=0; i<size(); i++) {
347  auto& e = (*this)[i];
348  e[0] = hex_digits[(i >> 4) & 0x0F];
349  e[1] = hex_digits[i & 0x0F];
350  }
351  }
352 private:
353  static constexpr const char* hex_digits = "0123456789abcdef";
354 };
355 
356 OPENDHT_PUBLIC extern const HexMap hex_map;
357 
358 inline std::string
359 toHex(const uint8_t* data, size_t size) {
360  std::string ret(size * 2, '\0');
361  for (size_t i=0; i<size; i++) {
362  auto b = ret.data()+i*2;
363  const auto& m = hex_map[data[i]];
364  *((uint16_t*)b) = *((uint16_t*)&m);
365  }
366  return ret;
367 }
368 
369 inline std::string
370 toHex(const std::vector<uint8_t>& data) {
371  return toHex(data.data(), data.size());
372 }
373 
374 template <size_t N>
375 const char*
376 Hash<N>::to_c_str() const
377 {
378  alignas(std::max_align_t) thread_local std::array<char, N*2+1> buf;
379  for (size_t i=0; i<N; i++) {
380  auto b = buf.data()+i*2;
381  const auto& m = hex_map[data_[i]];
382  *((uint16_t*)b) = *((uint16_t*)&m);
383  }
384  return buf.data();
385 }
386 
387 template <size_t N>
388 std::string
389 Hash<N>::toString() const
390 {
391  return std::string(to_c_str(), N*2);
392 }
393 
394 struct OPENDHT_PUBLIC NodeExport {
395  InfoHash id;
396  sockaddr_storage ss;
397  socklen_t sslen;
398 
399  template <typename Packer>
400  void msgpack_pack(Packer& pk) const
401  {
402  pk.pack_map(2);
403  pk.pack(std::string("id"));
404  pk.pack(id);
405  pk.pack(std::string("addr"));
406  pk.pack_bin(sslen);
407  pk.pack_bin_body((char*)&ss, sslen);
408  }
409 
410  void msgpack_unpack(msgpack::object o);
411 
412  OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const NodeExport& h);
413 };
414 
415 }
OPENDHT_PUBLIC Blob hash(const Blob &data, size_t hash_length=512/8)
int lowbit() const
Definition: infohash.h:149
static unsigned commonBits(const Hash &id1, const Hash &id2)
Definition: infohash.h:168
Hash(std::string_view hex)
Definition: infohash.h:84
int xorCmp(const Hash &id1, const Hash &id2) const
Definition: infohash.h:193
Definition: callbacks.h:35