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