/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 |