Coverage Report

Created: 2026-06-15 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtorrent/src/kademlia/node_id.cpp
Line
Count
Source
1
/*
2
3
Copyright (c) 2006-2008, 2010-2016, 2018, 2020-2022, Arvid Norberg
4
Copyright (c) 2016, 2018, 2020-2021, Alden Torres
5
Copyright (c) 2016, Steven Siloti
6
All rights reserved.
7
8
You may use, distribute and modify this code under the terms of the BSD license,
9
see LICENSE file.
10
*/
11
12
#include <algorithm>
13
14
#include "libtorrent/kademlia/node_id.hpp"
15
#include "libtorrent/kademlia/node_entry.hpp"
16
#include "libtorrent/assert.hpp"
17
#include "libtorrent/aux_/ip_helpers.hpp" // for is_local et.al
18
#include "libtorrent/aux_/random.hpp" // for random
19
#include "libtorrent/hasher.hpp" // for hasher
20
#include "libtorrent/aux_/crc32c.hpp" // for crc32c
21
22
namespace libtorrent::dht {
23
24
// returns the distance between the two nodes
25
// using the kademlia XOR-metric
26
node_id distance(node_id const& n1, node_id const& n2)
27
19
{
28
19
  return n1 ^ n2;
29
19
}
30
31
// returns true if: distance(n1, ref) < distance(n2, ref)
32
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
33
0
{
34
0
  node_id const lhs = n1 ^ ref;
35
0
  node_id const rhs = n2 ^ ref;
36
0
  return lhs < rhs;
37
0
}
38
39
// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
40
// useful for finding out which bucket a node belongs to
41
int distance_exp(node_id const& n1, node_id const& n2)
42
19
{
43
  // TODO: it's a little bit weird to return 159 - leading zeroes. It should
44
  // probably be 160 - leading zeroes, but all other code in here is tuned to
45
  // this expectation now, and it doesn't really matter (other than complexity)
46
19
  return std::max(159 - distance(n1, n2).count_leading_zeroes(), 0);
47
19
}
48
49
int min_distance_exp(node_id const& n1, std::vector<node_id> const& ids)
50
0
{
51
0
  TORRENT_ASSERT(ids.size() > 0);
52
53
0
  int min = 160; // see distance_exp for the why of this constant
54
0
  for (auto const& node_id : ids)
55
0
  {
56
0
    min = std::min(min, distance_exp(n1, node_id));
57
0
  }
58
59
0
  return min;
60
0
}
61
62
node_id generate_id_impl(address const& ip_, std::uint32_t r)
63
19
{
64
19
  std::uint8_t* ip = nullptr;
65
66
19
  static std::uint8_t const v4mask[] = { 0x03, 0x0f, 0x3f, 0xff };
67
19
  static std::uint8_t const v6mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
68
19
  std::uint8_t const* mask = nullptr;
69
19
  int num_octets = 0;
70
71
19
  address_v4::bytes_type b4{};
72
19
  address_v6::bytes_type b6{};
73
19
  if (ip_.is_v6())
74
0
  {
75
0
    b6 = ip_.to_v6().to_bytes();
76
0
    ip = b6.data();
77
0
    num_octets = 8;
78
0
    mask = v6mask;
79
0
  }
80
19
  else
81
19
  {
82
19
    b4 = ip_.to_v4().to_bytes();
83
19
    ip = b4.data();
84
19
    num_octets = 4;
85
19
    mask = v4mask;
86
19
  }
87
88
95
  for (int i = 0; i < num_octets; ++i)
89
76
    ip[i] &= mask[i];
90
91
19
  ip[0] |= (r & 0x7) << 5;
92
93
  // this is the crc32c (Castagnoli) polynomial
94
19
  std::uint32_t c;
95
19
  if (num_octets == 4)
96
19
  {
97
19
    c = aux::crc32c_32(*reinterpret_cast<std::uint32_t*>(ip));
98
19
  }
99
0
  else
100
0
  {
101
0
    TORRENT_ASSERT(num_octets == 8);
102
0
    c = aux::crc32c(reinterpret_cast<std::uint64_t*>(ip), 1);
103
0
  }
104
19
  node_id id;
105
106
19
  id[0] = (c >> 24) & 0xff;
107
19
  id[1] = (c >> 16) & 0xff;
108
19
  id[2] = (((c >> 8) & 0xf8) | aux::random(0x7)) & 0xff;
109
110
323
  for (int i = 3; i < 19; ++i) id[i] = aux::random(0xff) & 0xff;
111
19
  id[19] = r & 0xff;
112
113
19
  return id;
114
19
}
115
116
static std::uint32_t secret = 0;
117
118
void make_id_secret(node_id& in)
119
0
{
120
0
  if (secret == 0) secret = aux::random(0xfffffffe) + 1;
121
122
0
  std::uint32_t const rand = aux::random(0xffffffff);
123
124
  // generate the last 4 bytes as a "signature" of the previous 4 bytes. This
125
  // lets us verify whether a hash came from this function or not in the future.
126
0
  hasher h(reinterpret_cast<char const*>(&secret), 4);
127
0
  h.update(reinterpret_cast<char const*>(&rand), 4);
128
0
  sha1_hash const secret_hash = h.final();
129
0
  std::memcpy(&in[20 - 4], &secret_hash[0], 4);
130
0
  std::memcpy(&in[20 - 8], &rand, 4);
131
0
}
132
133
node_id generate_random_id()
134
1
{
135
1
  char r[20];
136
1
  aux::random_bytes(r);
137
1
  return hasher(r, 20).final();
138
1
}
139
140
node_id generate_secret_id()
141
0
{
142
0
  node_id ret = generate_random_id();
143
0
  make_id_secret(ret);
144
0
  return ret;
145
0
}
146
147
bool verify_secret_id(node_id const& nid)
148
0
{
149
0
  if (secret == 0) return false;
150
151
0
  hasher h(reinterpret_cast<char*>(&secret), 4);
152
0
  h.update(reinterpret_cast<char const*>(&nid[20 - 8]), 4);
153
0
  sha1_hash secret_hash = h.final();
154
0
  return std::memcmp(&nid[20 - 4], &secret_hash[0], 4) == 0;
155
0
}
156
157
// verifies whether a node-id matches the IP it's used from
158
// returns true if the node-id is OK coming from this source
159
// and false otherwise.
160
bool verify_id(node_id const& nid, address const& source_ip)
161
19
{
162
  // no need to verify local IPs, they would be incorrect anyway
163
19
  if (aux::is_local(source_ip)) return true;
164
165
19
  node_id h = generate_id_impl(source_ip, nid[19]);
166
19
  return nid[0] == h[0] && nid[1] == h[1] && (nid[2] & 0xf8) == (h[2] & 0xf8);
167
19
}
168
169
node_id generate_id(address const& ip)
170
0
{
171
0
  return generate_id_impl(ip, aux::random(0xffffffff));
172
0
}
173
174
bool matching_prefix(node_id const& nid, int mask, int prefix, int offset)
175
0
{
176
0
  node_id id = nid;
177
0
  id <<= offset;
178
0
  return (id[0] & mask) == prefix;
179
0
}
180
181
node_id generate_prefix_mask(int const bits)
182
0
{
183
0
  TORRENT_ASSERT(bits >= 0);
184
0
  TORRENT_ASSERT(bits <= 160);
185
0
  node_id mask;
186
0
  std::size_t b = 0;
187
#if defined __GNUC__ && __GNUC__ == 12
188
#pragma GCC diagnostic push
189
#pragma GCC diagnostic ignored "-Wstringop-overflow"
190
#endif
191
0
  for (; int(b) < bits - 7; b += 8) mask[b / 8] |= 0xff;
192
#if defined __GNUC__ && __GNUC__ == 12
193
#pragma GCC diagnostic pop
194
#endif
195
0
  if (bits < 160) mask[b / 8] |= (0xff << (8 - (bits & 7))) & 0xff;
196
0
  return mask;
197
0
}
198
199
} // namespace libtorrent::dht