Coverage Report

Created: 2025-08-28 06:22

/src/libtorrent/src/kademlia/get_peers.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
3
Copyright (c) 2006, Daniel Wallin
4
Copyright (c) 2013-2020, Arvid Norberg
5
Copyright (c) 2015, Steven Siloti
6
Copyright (c) 2015, Thomas Yuan
7
Copyright (c) 2016-2017, Alden Torres
8
Copyright (c) 2016-2017, Pavel Pimenov
9
All rights reserved.
10
11
Redistribution and use in source and binary forms, with or without
12
modification, are permitted provided that the following conditions
13
are met:
14
15
    * Redistributions of source code must retain the above copyright
16
      notice, this list of conditions and the following disclaimer.
17
    * Redistributions in binary form must reproduce the above copyright
18
      notice, this list of conditions and the following disclaimer in
19
      the documentation and/or other materials provided with the distribution.
20
    * Neither the name of the author nor the names of its
21
      contributors may be used to endorse or promote products derived
22
      from this software without specific prior written permission.
23
24
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
POSSIBILITY OF SUCH DAMAGE.
35
36
*/
37
38
#include <libtorrent/kademlia/get_peers.hpp>
39
#include <libtorrent/kademlia/node.hpp>
40
#include <libtorrent/kademlia/dht_observer.hpp>
41
#include <libtorrent/socket_io.hpp>
42
#include <libtorrent/performance_counters.hpp>
43
#include <libtorrent/aux_/ip_helpers.hpp> // for is_v4
44
45
#ifndef TORRENT_DISABLE_LOGGING
46
#include <libtorrent/hex.hpp> // to_hex
47
#endif
48
49
namespace libtorrent { namespace dht {
50
51
void get_peers_observer::reply(msg const& m)
52
0
{
53
0
  bdecode_node const r = m.message.dict_find_dict("r");
54
0
  if (!r)
55
0
  {
56
#ifndef TORRENT_DISABLE_LOGGING
57
    get_observer()->log(dht_logger::traversal, "[%u] missing response dict"
58
      , algorithm()->id());
59
#endif
60
0
    timeout();
61
0
    return;
62
0
  }
63
64
  // look for peers
65
0
  bdecode_node const n = r.dict_find_list("values");
66
0
  if (n)
67
0
  {
68
0
    std::vector<tcp::endpoint> peer_list;
69
0
    if (n.list_size() == 1 && n.list_at(0).type() == bdecode_node::string_t
70
0
      && aux::is_v4(m.addr))
71
0
    {
72
      // assume it's mainline format
73
0
      char const* peers = n.list_at(0).string_ptr();
74
0
      char const* end = peers + n.list_at(0).string_length();
75
76
#ifndef TORRENT_DISABLE_LOGGING
77
      log_peers(m, r, int((end - peers) / 6));
78
#endif
79
0
      while (end - peers >= 6)
80
0
        peer_list.push_back(aux::read_v4_endpoint<tcp::endpoint>(peers));
81
0
    }
82
0
    else
83
0
    {
84
      // assume it's uTorrent/libtorrent format
85
0
      peer_list = aux::read_endpoint_list<tcp::endpoint>(n);
86
#ifndef TORRENT_DISABLE_LOGGING
87
      log_peers(m, r, n.list_size());
88
#endif
89
0
    }
90
0
    static_cast<get_peers*>(algorithm())->got_peers(peer_list);
91
0
  }
92
93
0
  find_data_observer::reply(m);
94
0
}
95
#ifndef TORRENT_DISABLE_LOGGING
96
void get_peers_observer::log_peers(msg const& m, bdecode_node const& r, int const size) const
97
{
98
      auto logger = get_observer();
99
      if (logger != nullptr && logger->should_log(dht_logger::traversal))
100
      {
101
        bdecode_node const id = r.dict_find_string("id");
102
        if (id && id.string_length() == 20)
103
        {
104
          logger->log(dht_logger::traversal, "[%u] PEERS "
105
            "invoke-count: %d branch-factor: %d addr: %s id: %s distance: %d p: %d"
106
            , algorithm()->id()
107
            , algorithm()->invoke_count()
108
            , algorithm()->branch_factor()
109
            , print_endpoint(m.addr).c_str()
110
            , aux::to_hex({id.string_ptr(), id.string_length()}).c_str()
111
            , distance_exp(algorithm()->target(), node_id(id.string_ptr()))
112
            , size);
113
        }
114
      }
115
}
116
#endif
117
void get_peers::got_peers(std::vector<tcp::endpoint> const& peers)
118
0
{
119
0
  if (m_data_callback) m_data_callback(peers);
120
0
}
121
122
get_peers::get_peers(
123
  node& dht_node
124
  , node_id const& target
125
  , data_callback dcallback
126
  , nodes_callback ncallback
127
  , bool noseeds)
128
0
  : find_data(dht_node, target, std::move(ncallback))
129
0
  , m_data_callback(std::move(dcallback))
130
0
  , m_noseeds(noseeds)
131
0
{
132
0
}
133
134
0
char const* get_peers::name() const { return "get_peers"; }
135
136
bool get_peers::invoke(observer_ptr o)
137
0
{
138
0
  if (m_done) return false;
139
140
0
  entry e;
141
0
  e["y"] = "q";
142
0
  entry& a = e["a"];
143
144
0
  e["q"] = "get_peers";
145
0
  a["info_hash"] = target().to_string();
146
0
  if (m_noseeds) a["noseed"] = 1;
147
148
0
  if (m_node.observer() != nullptr)
149
0
  {
150
0
    m_node.observer()->outgoing_get_peers(target(), target(), o->target_ep());
151
0
  }
152
153
0
  m_node.stats_counters().inc_stats_counter(counters::dht_get_peers_out);
154
155
0
  return m_node.m_rpc.invoke(e, o->target_ep(), o);
156
0
}
157
158
observer_ptr get_peers::new_observer(udp::endpoint const& ep
159
  , node_id const& id)
160
0
{
161
0
  auto o = m_node.m_rpc.allocate_observer<get_peers_observer>(self(), ep, id);
162
0
#if TORRENT_USE_ASSERTS
163
0
  if (o) o->m_in_constructor = false;
164
0
#endif
165
0
  return o;
166
0
}
167
168
obfuscated_get_peers::obfuscated_get_peers(
169
  node& dht_node
170
  , node_id const& target
171
  , data_callback dcallback
172
  , nodes_callback ncallback
173
  , bool noseeds)
174
0
  : get_peers(dht_node, target, std::move(dcallback), std::move(ncallback), noseeds)
175
0
  , m_obfuscated(true)
176
0
{
177
0
}
178
179
char const* obfuscated_get_peers::name() const
180
0
{ return !m_obfuscated ? get_peers::name() : "get_peers [obfuscated]"; }
181
182
observer_ptr obfuscated_get_peers::new_observer(udp::endpoint const& ep
183
  , node_id const& id)
184
0
{
185
0
  if (m_obfuscated)
186
0
  {
187
0
    auto o = m_node.m_rpc.allocate_observer<obfuscated_get_peers_observer>(self()
188
0
      , ep, id);
189
0
#if TORRENT_USE_ASSERTS
190
0
    if (o) o->m_in_constructor = false;
191
0
#endif
192
0
    return o;
193
0
  }
194
0
  else
195
0
  {
196
0
    auto o = m_node.m_rpc.allocate_observer<get_peers_observer>(self()
197
0
      , ep, id);
198
0
#if TORRENT_USE_ASSERTS
199
0
    if (o) o->m_in_constructor = false;
200
0
#endif
201
0
    return o;
202
0
  }
203
0
}
204
205
bool obfuscated_get_peers::invoke(observer_ptr o)
206
0
{
207
0
  if (!m_obfuscated) return get_peers::invoke(o);
208
209
0
  node_id const& id = o->id();
210
0
  int const shared_prefix = 160 - distance_exp(id, target());
211
212
0
  entry e;
213
0
  e["y"] = "q";
214
0
  e["q"] = "get_peers";
215
0
  entry& a = e["a"];
216
217
  // This logic will obfuscate the target info-hash
218
  // we're looking up, in order to preserve more privacy
219
  // on the DHT. This is done by only including enough
220
  // bits in the info-hash for the node we're querying to
221
  // give a good answer, but not more.
222
223
  // now, obfuscate the bits past shared_prefix + 3
224
0
  node_id mask = generate_prefix_mask(shared_prefix + 3);
225
0
  node_id obfuscated_target = generate_random_id() & ~mask;
226
0
  obfuscated_target |= target() & mask;
227
0
  a["info_hash"] = obfuscated_target.to_string();
228
229
0
  if (m_node.observer() != nullptr)
230
0
  {
231
0
    m_node.observer()->outgoing_get_peers(target(), obfuscated_target
232
0
      , o->target_ep());
233
0
  }
234
235
0
  m_node.stats_counters().inc_stats_counter(counters::dht_get_peers_out);
236
237
0
  return m_node.m_rpc.invoke(e, o->target_ep(), o);
238
0
}
239
240
void obfuscated_get_peers::done()
241
0
{
242
0
  if (!m_obfuscated) return get_peers::done();
243
244
  // oops, we failed to switch over to the non-obfuscated
245
  // mode early enough. do it now
246
247
0
  auto ta = std::make_shared<get_peers>(m_node, target()
248
0
    , m_data_callback, m_nodes_callback, m_noseeds);
249
250
  // don't call these when the obfuscated_get_peers
251
  // is done, we're passing them on to be called when
252
  // ta completes.
253
0
  m_data_callback = nullptr;
254
0
  m_nodes_callback = nullptr;
255
256
#ifndef TORRENT_DISABLE_LOGGING
257
    get_node().observer()->log(dht_logger::traversal, "[%u] obfuscated get_peers "
258
      "phase 1 done, spawning get_peers [ %u ]"
259
      , id(), ta->id());
260
#endif
261
262
0
  int num_added = 0;
263
0
  for (auto i = m_results.begin()
264
0
    , end(m_results.end()); i != end && num_added < 16; ++i)
265
0
  {
266
0
    observer_ptr o = *i;
267
268
    // only add nodes whose node ID we know and that
269
    // we know are alive
270
0
    if (o->flags & observer::flag_no_id) continue;
271
0
    if (!(o->flags & observer::flag_alive)) continue;
272
273
0
    ta->add_entry(o->id(), o->target_ep(), observer::flag_initial);
274
0
    ++num_added;
275
0
  }
276
277
0
  ta->start();
278
279
0
  get_peers::done();
280
0
}
281
282
void obfuscated_get_peers_observer::reply(msg const& m)
283
0
{
284
0
  bdecode_node const r = m.message.dict_find_dict("r");
285
0
  if (!r)
286
0
  {
287
#ifndef TORRENT_DISABLE_LOGGING
288
    get_observer()->log(dht_logger::traversal, "[%u] missing response dict"
289
      , algorithm()->id());
290
#endif
291
0
    timeout();
292
0
    return;
293
0
  }
294
295
0
  bdecode_node const id = r.dict_find_string("id");
296
0
  if (!id || id.string_length() != 20)
297
0
  {
298
#ifndef TORRENT_DISABLE_LOGGING
299
    get_observer()->log(dht_logger::traversal, "[%u] invalid id in response"
300
      , algorithm()->id());
301
#endif
302
0
    timeout();
303
0
    return;
304
0
  }
305
306
0
  traversal_observer::reply(m);
307
308
0
  done();
309
0
}
310
311
} } // namespace libtorrent::dht