Coverage Report

Created: 2025-06-12 06:27

/src/libtorrent/src/kademlia/node_id.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
3
Copyright (c) 2006-2008, 2010-2020, Arvid Norberg
4
Copyright (c) 2016, 2018, Alden Torres
5
Copyright (c) 2016, Steven Siloti
6
All rights reserved.
7
8
Redistribution and use in source and binary forms, with or without
9
modification, are permitted provided that the following conditions
10
are met:
11
12
    * Redistributions of source code must retain the above copyright
13
      notice, this list of conditions and the following disclaimer.
14
    * Redistributions in binary form must reproduce the above copyright
15
      notice, this list of conditions and the following disclaimer in
16
      the documentation and/or other materials provided with the distribution.
17
    * Neither the name of the author nor the names of its
18
      contributors may be used to endorse or promote products derived
19
      from this software without specific prior written permission.
20
21
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
POSSIBILITY OF SUCH DAMAGE.
32
33
*/
34
35
#include <algorithm>
36
37
#include "libtorrent/kademlia/node_id.hpp"
38
#include "libtorrent/kademlia/node_entry.hpp"
39
#include "libtorrent/assert.hpp"
40
#include "libtorrent/aux_/ip_helpers.hpp" // for is_local et.al
41
#include "libtorrent/random.hpp" // for random
42
#include "libtorrent/hasher.hpp" // for hasher
43
#include "libtorrent/crc32c.hpp" // for crc32c
44
45
namespace libtorrent { namespace dht {
46
47
// returns the distance between the two nodes
48
// using the kademlia XOR-metric
49
node_id distance(node_id const& n1, node_id const& n2)
50
34
{
51
34
  return n1 ^ n2;
52
34
}
53
54
// returns true if: distance(n1, ref) < distance(n2, ref)
55
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
56
0
{
57
0
  node_id const lhs = n1 ^ ref;
58
0
  node_id const rhs = n2 ^ ref;
59
0
  return lhs < rhs;
60
0
}
61
62
// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
63
// useful for finding out which bucket a node belongs to
64
int distance_exp(node_id const& n1, node_id const& n2)
65
34
{
66
  // TODO: it's a little bit weird to return 159 - leading zeroes. It should
67
  // probably be 160 - leading zeroes, but all other code in here is tuned to
68
  // this expectation now, and it doesn't really matter (other than complexity)
69
34
  return std::max(159 - distance(n1, n2).count_leading_zeroes(), 0);
70
34
}
71
72
int min_distance_exp(node_id const& n1, std::vector<node_id> const& ids)
73
0
{
74
0
  TORRENT_ASSERT(ids.size() > 0);
75
76
0
  int min = 160; // see distance_exp for the why of this constant
77
0
  for (auto const& node_id : ids)
78
0
  {
79
0
    min = std::min(min, distance_exp(n1, node_id));
80
0
  }
81
82
0
  return min;
83
0
}
84
85
node_id generate_id_impl(address const& ip_, std::uint32_t r)
86
34
{
87
34
  std::uint8_t* ip = nullptr;
88
89
34
  static std::uint8_t const v4mask[] = { 0x03, 0x0f, 0x3f, 0xff };
90
34
  static std::uint8_t const v6mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
91
34
  std::uint8_t const* mask = nullptr;
92
34
  int num_octets = 0;
93
94
34
  address_v4::bytes_type b4{};
95
34
  address_v6::bytes_type b6{};
96
34
  if (ip_.is_v6())
97
0
  {
98
0
    b6 = ip_.to_v6().to_bytes();
99
0
    ip = b6.data();
100
0
    num_octets = 8;
101
0
    mask = v6mask;
102
0
  }
103
34
  else
104
34
  {
105
34
    b4 = ip_.to_v4().to_bytes();
106
34
    ip = b4.data();
107
34
    num_octets = 4;
108
34
    mask = v4mask;
109
34
  }
110
111
170
  for (int i = 0; i < num_octets; ++i)
112
136
    ip[i] &= mask[i];
113
114
34
  ip[0] |= (r & 0x7) << 5;
115
116
  // this is the crc32c (Castagnoli) polynomial
117
34
  std::uint32_t c;
118
34
  if (num_octets == 4)
119
34
  {
120
34
    c = crc32c_32(*reinterpret_cast<std::uint32_t*>(ip));
121
34
  }
122
0
  else
123
0
  {
124
0
    TORRENT_ASSERT(num_octets == 8);
125
0
    c = crc32c(reinterpret_cast<std::uint64_t*>(ip), 1);
126
0
  }
127
34
  node_id id;
128
129
34
  id[0] = (c >> 24) & 0xff;
130
34
  id[1] = (c >> 16) & 0xff;
131
34
  id[2] = (((c >> 8) & 0xf8) | random(0x7)) & 0xff;
132
133
578
  for (int i = 3; i < 19; ++i) id[i] = random(0xff) & 0xff;
134
34
  id[19] = r & 0xff;
135
136
34
  return id;
137
34
}
138
139
static std::uint32_t secret = 0;
140
141
void make_id_secret(node_id& in)
142
0
{
143
0
  if (secret == 0) secret = random(0xfffffffe) + 1;
144
145
0
  std::uint32_t const rand = random(0xffffffff);
146
147
  // generate the last 4 bytes as a "signature" of the previous 4 bytes. This
148
  // lets us verify whether a hash came from this function or not in the future.
149
0
  hasher h(reinterpret_cast<char const*>(&secret), 4);
150
0
  h.update(reinterpret_cast<char const*>(&rand), 4);
151
0
  sha1_hash const secret_hash = h.final();
152
0
  std::memcpy(&in[20 - 4], &secret_hash[0], 4);
153
0
  std::memcpy(&in[20 - 8], &rand, 4);
154
0
}
155
156
node_id generate_random_id()
157
1
{
158
1
  char r[20];
159
1
  aux::random_bytes(r);
160
1
  return hasher(r, 20).final();
161
1
}
162
163
node_id generate_secret_id()
164
0
{
165
0
  node_id ret = generate_random_id();
166
0
  make_id_secret(ret);
167
0
  return ret;
168
0
}
169
170
bool verify_secret_id(node_id const& nid)
171
0
{
172
0
  if (secret == 0) return false;
173
174
0
  hasher h(reinterpret_cast<char*>(&secret), 4);
175
0
  h.update(reinterpret_cast<char const*>(&nid[20 - 8]), 4);
176
0
  sha1_hash secret_hash = h.final();
177
0
  return std::memcmp(&nid[20 - 4], &secret_hash[0], 4) == 0;
178
0
}
179
180
// verifies whether a node-id matches the IP it's used from
181
// returns true if the node-id is OK coming from this source
182
// and false otherwise.
183
bool verify_id(node_id const& nid, address const& source_ip)
184
34
{
185
  // no need to verify local IPs, they would be incorrect anyway
186
34
  if (aux::is_local(source_ip)) return true;
187
188
34
  node_id h = generate_id_impl(source_ip, nid[19]);
189
34
  return nid[0] == h[0] && nid[1] == h[1] && (nid[2] & 0xf8) == (h[2] & 0xf8);
190
34
}
191
192
node_id generate_id(address const& ip)
193
0
{
194
0
  return generate_id_impl(ip, random(0xffffffff));
195
0
}
196
197
bool matching_prefix(node_id const& nid, int mask, int prefix, int offset)
198
0
{
199
0
  node_id id = nid;
200
0
  id <<= offset;
201
0
  return (id[0] & mask) == prefix;
202
0
}
203
204
node_id generate_prefix_mask(int const bits)
205
0
{
206
0
  TORRENT_ASSERT(bits >= 0);
207
0
  TORRENT_ASSERT(bits <= 160);
208
0
  node_id mask;
209
0
  std::size_t b = 0;
210
#if defined __GNUC__ && __GNUC__ == 12
211
#pragma GCC diagnostic push
212
#pragma GCC diagnostic ignored "-Wstringop-overflow"
213
#endif
214
0
  for (; int(b) < bits - 7; b += 8) mask[b / 8] |= 0xff;
215
#if defined __GNUC__ && __GNUC__ == 12
216
#pragma GCC diagnostic pop
217
#endif
218
0
  if (bits < 160) mask[b / 8] |= (0xff << (8 - (bits & 7))) & 0xff;
219
0
  return mask;
220
0
}
221
222
} }  // namespace libtorrent::dht