Coverage Report

Created: 2025-10-10 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtorrent/src/kademlia/get_item.cpp
Line
Count
Source
1
/*
2
3
Copyright (c) 2013, Steven Siloti
4
Copyright (c) 2015, Thomas
5
Copyright (c) 2013-2019, Arvid Norberg
6
Copyright (c) 2015, Thomas Yuan
7
Copyright (c) 2016-2017, Alden Torres
8
Copyright (c) 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/config.hpp>
39
#include <libtorrent/bdecode.hpp>
40
#include <libtorrent/kademlia/get_item.hpp>
41
#include <libtorrent/kademlia/node.hpp>
42
#include <libtorrent/kademlia/dht_observer.hpp>
43
#include <libtorrent/performance_counters.hpp>
44
45
namespace libtorrent { namespace dht {
46
47
void get_item::got_data(bdecode_node const& v,
48
  public_key const& pk,
49
  sequence_number const seq,
50
  signature const& sig)
51
0
{
52
  // we received data!
53
  // if no data_callback, we needn't care about the data we get.
54
  // only put_immutable_item no data_callback
55
0
  if (!m_data_callback) return;
56
57
  // for get_immutable_item
58
0
  if (m_immutable)
59
0
  {
60
    // If m_data isn't empty, we should have post alert.
61
0
    if (!m_data.empty()) return;
62
63
0
    sha1_hash incoming_target = item_target_id(v.data_section());
64
0
    if (incoming_target != target()) return;
65
66
0
    m_data.assign(v);
67
68
    // There can only be one true immutable item with a given id
69
    // Now that we've got it and the user doesn't want to do a put
70
    // there's no point in continuing to query other nodes
71
0
    m_data_callback(m_data, true);
72
0
    done();
73
74
0
    return;
75
0
  }
76
77
  // immutable data should have been handled before this line, only mutable
78
  // data can reach here, which means pk, sig and seq must be valid.
79
80
0
  std::string const salt_copy(m_data.salt());
81
0
  sha1_hash const incoming_target = item_target_id(salt_copy, pk);
82
0
  if (incoming_target != target()) return;
83
84
  // this is mutable data. If it passes the signature
85
  // check, remember it. Just keep the version with
86
  // the highest sequence number.
87
0
  if (m_data.empty() || m_data.seq() < seq)
88
0
  {
89
0
    if (!m_data.assign(v, salt_copy, seq, pk, sig))
90
0
      return;
91
92
    // for get_item, we should call callback when we get data,
93
    // even if the date is not authoritative, we can update later.
94
    // so caller can get response ASAP without waiting transaction
95
    // time-out (15 seconds).
96
    // for put_item, the callback function will do nothing
97
    // if the data is non-authoritative.
98
0
    m_data_callback(m_data, false);
99
0
  }
100
0
}
101
102
get_item::get_item(
103
  node& dht_node
104
  , node_id const& target
105
  , data_callback dcallback
106
  , nodes_callback ncallback)
107
0
  : find_data(dht_node, target, std::move(ncallback))
108
0
  , m_data_callback(std::move(dcallback))
109
0
  , m_immutable(true)
110
0
{
111
0
}
112
113
get_item::get_item(
114
  node& dht_node
115
  , public_key const& pk
116
  , span<char const> salt
117
  , data_callback dcallback
118
  , nodes_callback ncallback)
119
0
  : find_data(dht_node, item_target_id(salt, pk), std::move(ncallback))
120
0
  , m_data_callback(std::move(dcallback))
121
0
  , m_data(pk, salt)
122
0
  , m_immutable(false)
123
0
{
124
0
}
125
126
0
char const* get_item::name() const { return "get"; }
127
128
observer_ptr get_item::new_observer(udp::endpoint const& ep
129
  , node_id const& id)
130
0
{
131
0
  auto o = m_node.m_rpc.allocate_observer<get_item_observer>(self(), ep, id);
132
0
#if TORRENT_USE_ASSERTS
133
0
  if (o) o->m_in_constructor = false;
134
0
#endif
135
0
  return o;
136
0
}
137
138
bool get_item::invoke(observer_ptr o)
139
0
{
140
0
  if (m_done) return false;
141
142
0
  entry e;
143
0
  e["y"] = "q";
144
0
  entry& a = e["a"];
145
146
0
  e["q"] = "get";
147
0
  a["target"] = target().to_string();
148
149
0
  m_node.stats_counters().inc_stats_counter(counters::dht_get_out);
150
151
0
  return m_node.m_rpc.invoke(e, o->target_ep(), o);
152
0
}
153
154
void get_item::done()
155
0
{
156
  // no data_callback for immutable item put
157
0
  if (!m_data_callback) return find_data::done();
158
159
0
  if (m_data.is_mutable() || m_data.empty())
160
0
  {
161
    // for mutable data, now we have authoritative data since
162
    // we've heard from everyone, to be sure we got the
163
    // latest version of the data (i.e. highest sequence number)
164
0
    m_data_callback(m_data, true);
165
166
0
#if TORRENT_USE_ASSERTS
167
0
    if (m_data.is_mutable())
168
0
    {
169
0
      TORRENT_ASSERT(target() == item_target_id(m_data.salt(), m_data.pk()));
170
0
    }
171
0
#endif
172
0
  }
173
174
0
  find_data::done();
175
0
}
176
177
void get_item_observer::reply(msg const& m)
178
0
{
179
0
  public_key pk{};
180
0
  signature sig{};
181
0
  sequence_number seq{0};
182
183
0
  bdecode_node const r = m.message.dict_find_dict("r");
184
0
  if (!r)
185
0
  {
186
#ifndef TORRENT_DISABLE_LOGGING
187
    get_observer()->log(dht_logger::traversal, "[%p] missing response dict"
188
      , static_cast<void*>(algorithm()));
189
#endif
190
0
    timeout();
191
0
    return;
192
0
  }
193
194
0
  bdecode_node const k = r.dict_find_string("k");
195
0
  if (k && k.string_length() == public_key::len)
196
0
    std::memcpy(pk.bytes.data(), k.string_ptr(), public_key::len);
197
198
0
  bdecode_node const s = r.dict_find_string("sig");
199
0
  if (s && s.string_length() == signature::len)
200
0
    std::memcpy(sig.bytes.data(), s.string_ptr(), signature::len);
201
202
0
  bdecode_node const q = r.dict_find_int("seq");
203
0
  if (q)
204
0
  {
205
0
    seq = sequence_number(q.int_value());
206
0
  }
207
0
  else if (k && s)
208
0
  {
209
0
    timeout();
210
0
    return;
211
0
  }
212
213
0
  bdecode_node v = r.dict_find("v");
214
0
  if (v)
215
0
  {
216
0
    static_cast<get_item*>(algorithm())->got_data(v, pk, seq, sig);
217
0
  }
218
219
0
  find_data_observer::reply(m);
220
0
}
221
222
} } // namespace libtorrent::dht