Coverage Report

Created: 2025-12-14 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtorrent/include/libtorrent/bencode.hpp
Line
Count
Source
1
/*
2
3
Copyright (c) 2003-2005, 2007-2009, 2012-2019, Arvid Norberg
4
Copyright (c) 2016, Alden Torres
5
Copyright (c) 2019, Amir Abrams
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
#ifndef TORRENT_BENCODE_HPP_INCLUDED
36
#define TORRENT_BENCODE_HPP_INCLUDED
37
38
// OVERVIEW
39
//
40
// Bencoding is a common representation in bittorrent used for dictionary,
41
// list, int and string hierarchies. It's used to encode .torrent files and
42
// some messages in the network protocol. libtorrent also uses it to store
43
// settings, resume data and other session state.
44
//
45
// Strings in bencoded structures do not necessarily represent text.
46
// Strings are raw byte buffers of a certain length. If a string is meant to be
47
// interpreted as text, it is required to be UTF-8 encoded. See `BEP 3`_.
48
//
49
// The function for decoding bencoded data bdecode(), returning a bdecode_node.
50
// This function builds a tree that points back into the original buffer. The
51
// returned bdecode_node will not be valid once the buffer it was parsed out of
52
// is discarded.
53
//
54
// It's possible to construct an entry from a bdecode_node, if a structure needs
55
// to be altered and re-encoded.
56
57
#include <string>
58
#include <iterator> // for distance
59
60
#include "libtorrent/config.hpp"
61
#include "libtorrent/entry.hpp"
62
#include "libtorrent/assert.hpp"
63
#include "libtorrent/io.hpp" // for write_string
64
#include "libtorrent/string_util.hpp" // for is_digit
65
66
namespace libtorrent {
67
68
#if TORRENT_ABI_VERSION == 1
69
  using invalid_encoding = system_error;
70
#endif
71
72
namespace aux {
73
74
  template <class OutIt, class In, typename Cond
75
    = typename std::enable_if<std::is_integral<In>::value>::type>
76
  int write_integer(OutIt& out, In data)
77
3.11M
  {
78
3.11M
    entry::integer_type const val = entry::integer_type(data);
79
3.11M
    TORRENT_ASSERT(data == In(val));
80
    // the stack allocated buffer for keeping the
81
    // decimal representation of the number can
82
    // not hold number bigger than this:
83
3.11M
    static_assert(sizeof(entry::integer_type) <= 8, "64 bit integers required");
84
3.11M
    static_assert(sizeof(data) <= sizeof(entry::integer_type), "input data too big, see entry::integer_type");
85
3.11M
    std::array<char, 21> buf;
86
3.11M
    auto const str = integer_to_str(buf, val);
87
3.11M
    for (char const c : str)
88
3.33M
    {
89
3.33M
      *out = c;
90
3.33M
      ++out;
91
3.33M
    }
92
3.11M
    return static_cast<int>(str.size());
93
3.11M
  }
int libtorrent::aux::write_integer<std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > >, long, void>(std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > >&, long)
Line
Count
Source
77
151k
  {
78
151k
    entry::integer_type const val = entry::integer_type(data);
79
151k
    TORRENT_ASSERT(data == In(val));
80
    // the stack allocated buffer for keeping the
81
    // decimal representation of the number can
82
    // not hold number bigger than this:
83
151k
    static_assert(sizeof(entry::integer_type) <= 8, "64 bit integers required");
84
151k
    static_assert(sizeof(data) <= sizeof(entry::integer_type), "input data too big, see entry::integer_type");
85
151k
    std::array<char, 21> buf;
86
151k
    auto const str = integer_to_str(buf, val);
87
151k
    for (char const c : str)
88
190k
    {
89
190k
      *out = c;
90
190k
      ++out;
91
190k
    }
92
151k
    return static_cast<int>(str.size());
93
151k
  }
int libtorrent::aux::write_integer<std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > >, unsigned long, void>(std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > >&, unsigned long)
Line
Count
Source
77
2.96M
  {
78
2.96M
    entry::integer_type const val = entry::integer_type(data);
79
2.96M
    TORRENT_ASSERT(data == In(val));
80
    // the stack allocated buffer for keeping the
81
    // decimal representation of the number can
82
    // not hold number bigger than this:
83
2.96M
    static_assert(sizeof(entry::integer_type) <= 8, "64 bit integers required");
84
2.96M
    static_assert(sizeof(data) <= sizeof(entry::integer_type), "input data too big, see entry::integer_type");
85
2.96M
    std::array<char, 21> buf;
86
2.96M
    auto const str = integer_to_str(buf, val);
87
2.96M
    for (char const c : str)
88
3.14M
    {
89
3.14M
      *out = c;
90
3.14M
      ++out;
91
3.14M
    }
92
2.96M
    return static_cast<int>(str.size());
93
2.96M
  }
Unexecuted instantiation: int libtorrent::aux::write_integer<std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > >, long, void>(std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > >&, long)
Unexecuted instantiation: int libtorrent::aux::write_integer<std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > >, unsigned long, void>(std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > >&, unsigned long)
Unexecuted instantiation: int libtorrent::aux::write_integer<char*, long, void>(char*&, long)
Unexecuted instantiation: int libtorrent::aux::write_integer<char*, unsigned long, void>(char*&, unsigned long)
Unexecuted instantiation: int libtorrent::aux::write_integer<std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, long, void>(std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, long)
Unexecuted instantiation: int libtorrent::aux::write_integer<std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, unsigned long, void>(std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, unsigned long)
94
95
  template <class OutIt>
96
  void write_char(OutIt& out, char c)
97
8.18M
  {
98
8.18M
    *out = c;
99
8.18M
    ++out;
100
8.18M
  }
void libtorrent::aux::write_char<std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > > >(std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > >&, char)
Line
Count
Source
97
8.18M
  {
98
8.18M
    *out = c;
99
8.18M
    ++out;
100
8.18M
  }
Unexecuted instantiation: void libtorrent::aux::write_char<std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > > >(std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > >&, char)
Unexecuted instantiation: void libtorrent::aux::write_char<char*>(char*&, char)
Unexecuted instantiation: void libtorrent::aux::write_char<std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >(std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, char)
101
102
  template <class InIt>
103
  std::string read_until(InIt& in, InIt end, char end_token, bool& err)
104
  {
105
    std::string ret;
106
    if (in == end)
107
    {
108
      err = true;
109
      return ret;
110
    }
111
    while (*in != end_token)
112
    {
113
      ret += *in;
114
      ++in;
115
      if (in == end)
116
      {
117
        err = true;
118
        return ret;
119
      }
120
    }
121
    return ret;
122
  }
123
124
  template<class InIt>
125
  void read_string(InIt& in, InIt end, int len, std::string& str, bool& err)
126
  {
127
    TORRENT_ASSERT(len >= 0);
128
    for (int i = 0; i < len; ++i)
129
    {
130
      if (in == end)
131
      {
132
        err = true;
133
        return;
134
      }
135
      str += *in;
136
      ++in;
137
    }
138
  }
139
140
  template<class OutIt>
141
  int bencode_recursive(OutIt& out, const entry& e)
142
5.37M
  {
143
5.37M
    int ret = 0;
144
5.37M
    switch(e.type())
145
5.37M
    {
146
151k
    case entry::int_t:
147
151k
      write_char(out, 'i');
148
151k
      ret += write_integer(out, e.integer());
149
151k
      write_char(out, 'e');
150
151k
      ret += 2;
151
151k
      break;
152
2.75M
    case entry::string_t:
153
2.75M
      ret += write_integer(out, e.string().length());
154
2.75M
      write_char(out, ':');
155
2.75M
      ret += write_string(e.string(), out);
156
2.75M
      ret += 1;
157
2.75M
      break;
158
22.5k
    case entry::list_t:
159
22.5k
      write_char(out, 'l');
160
22.5k
      for (auto const& i : e.list())
161
5.16M
        ret += bencode_recursive(out, i);
162
22.5k
      write_char(out, 'e');
163
22.5k
      ret += 2;
164
22.5k
      break;
165
5.07k
    case entry::dictionary_t:
166
5.07k
      write_char(out, 'd');
167
5.07k
      for (auto const& i : e.dict())
168
202k
      {
169
        // write key
170
202k
        ret += write_integer(out, i.first.length());
171
202k
        write_char(out, ':');
172
202k
        ret += write_string(i.first, out);
173
        // write value
174
202k
        ret += bencode_recursive(out, i.second);
175
202k
        ret += 1;
176
202k
      }
177
5.07k
      write_char(out, 'e');
178
5.07k
      ret += 2;
179
5.07k
      break;
180
66
    case entry::preformatted_t:
181
66
      std::copy(e.preformatted().begin(), e.preformatted().end(), out);
182
66
      ret += static_cast<int>(e.preformatted().size());
183
66
      break;
184
2.43M
    case entry::undefined_t:
185
186
      // empty string
187
2.43M
      write_char(out, '0');
188
2.43M
      write_char(out, ':');
189
190
2.43M
      ret += 2;
191
2.43M
      break;
192
5.37M
    }
193
5.37M
    return ret;
194
5.37M
  }
int libtorrent::aux::bencode_recursive<std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > > >(std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > >&, libtorrent::entry const&)
Line
Count
Source
142
5.37M
  {
143
5.37M
    int ret = 0;
144
5.37M
    switch(e.type())
145
5.37M
    {
146
151k
    case entry::int_t:
147
151k
      write_char(out, 'i');
148
151k
      ret += write_integer(out, e.integer());
149
151k
      write_char(out, 'e');
150
151k
      ret += 2;
151
151k
      break;
152
2.75M
    case entry::string_t:
153
2.75M
      ret += write_integer(out, e.string().length());
154
2.75M
      write_char(out, ':');
155
2.75M
      ret += write_string(e.string(), out);
156
2.75M
      ret += 1;
157
2.75M
      break;
158
22.5k
    case entry::list_t:
159
22.5k
      write_char(out, 'l');
160
22.5k
      for (auto const& i : e.list())
161
5.16M
        ret += bencode_recursive(out, i);
162
22.5k
      write_char(out, 'e');
163
22.5k
      ret += 2;
164
22.5k
      break;
165
5.07k
    case entry::dictionary_t:
166
5.07k
      write_char(out, 'd');
167
5.07k
      for (auto const& i : e.dict())
168
202k
      {
169
        // write key
170
202k
        ret += write_integer(out, i.first.length());
171
202k
        write_char(out, ':');
172
202k
        ret += write_string(i.first, out);
173
        // write value
174
202k
        ret += bencode_recursive(out, i.second);
175
202k
        ret += 1;
176
202k
      }
177
5.07k
      write_char(out, 'e');
178
5.07k
      ret += 2;
179
5.07k
      break;
180
66
    case entry::preformatted_t:
181
66
      std::copy(e.preformatted().begin(), e.preformatted().end(), out);
182
66
      ret += static_cast<int>(e.preformatted().size());
183
66
      break;
184
2.43M
    case entry::undefined_t:
185
186
      // empty string
187
2.43M
      write_char(out, '0');
188
2.43M
      write_char(out, ':');
189
190
2.43M
      ret += 2;
191
2.43M
      break;
192
5.37M
    }
193
5.37M
    return ret;
194
5.37M
  }
Unexecuted instantiation: int libtorrent::aux::bencode_recursive<std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > > >(std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > >&, libtorrent::entry const&)
Unexecuted instantiation: int libtorrent::aux::bencode_recursive<char*>(char*&, libtorrent::entry const&)
Unexecuted instantiation: int libtorrent::aux::bencode_recursive<std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >(std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, libtorrent::entry const&)
195
#if TORRENT_ABI_VERSION == 1
196
  template<class InIt>
197
  void bdecode_recursive(InIt& in, InIt end, entry& ret, bool& err, int depth)
198
  {
199
    if (depth >= 100)
200
    {
201
      err = true;
202
      return;
203
    }
204
205
    if (in == end)
206
    {
207
      err = true;
208
#if TORRENT_USE_ASSERTS
209
      ret.m_type_queried = false;
210
#endif
211
      return;
212
    }
213
    switch (*in)
214
    {
215
216
    // ----------------------------------------------
217
    // integer
218
    case 'i':
219
      {
220
      ++in; // 'i'
221
      std::string const val = read_until(in, end, 'e', err);
222
      if (err) return;
223
      TORRENT_ASSERT(*in == 'e');
224
      ++in; // 'e'
225
      ret = entry(entry::int_t);
226
      char* end_pointer;
227
      ret.integer() = std::strtoll(val.c_str(), &end_pointer, 10);
228
#if TORRENT_USE_ASSERTS
229
      ret.m_type_queried = false;
230
#endif
231
      if (end_pointer == val.c_str())
232
      {
233
        err = true;
234
        return;
235
      }
236
      }
237
      break;
238
239
    // ----------------------------------------------
240
    // list
241
    case 'l':
242
      ret = entry(entry::list_t);
243
      ++in; // 'l'
244
      while (*in != 'e')
245
      {
246
        ret.list().emplace_back();
247
        entry& e = ret.list().back();
248
        bdecode_recursive(in, end, e, err, depth + 1);
249
        if (err)
250
        {
251
#if TORRENT_USE_ASSERTS
252
          ret.m_type_queried = false;
253
#endif
254
          return;
255
        }
256
        if (in == end)
257
        {
258
          err = true;
259
#if TORRENT_USE_ASSERTS
260
          ret.m_type_queried = false;
261
#endif
262
          return;
263
        }
264
      }
265
#if TORRENT_USE_ASSERTS
266
      ret.m_type_queried = false;
267
#endif
268
      TORRENT_ASSERT(*in == 'e');
269
      ++in; // 'e'
270
      break;
271
272
    // ----------------------------------------------
273
    // dictionary
274
    case 'd':
275
      ret = entry(entry::dictionary_t);
276
      ++in; // 'd'
277
      while (*in != 'e')
278
      {
279
        entry key;
280
        bdecode_recursive(in, end, key, err, depth + 1);
281
        if (err || key.type() != entry::string_t)
282
        {
283
#if TORRENT_USE_ASSERTS
284
          ret.m_type_queried = false;
285
#endif
286
          return;
287
        }
288
        entry& e = ret[key.string()];
289
        bdecode_recursive(in, end, e, err, depth + 1);
290
        if (err)
291
        {
292
#if TORRENT_USE_ASSERTS
293
          ret.m_type_queried = false;
294
#endif
295
          return;
296
        }
297
        if (in == end)
298
        {
299
          err = true;
300
#if TORRENT_USE_ASSERTS
301
          ret.m_type_queried = false;
302
#endif
303
          return;
304
        }
305
      }
306
#if TORRENT_USE_ASSERTS
307
      ret.m_type_queried = false;
308
#endif
309
      TORRENT_ASSERT(*in == 'e');
310
      ++in; // 'e'
311
      break;
312
313
    // ----------------------------------------------
314
    // string
315
    default:
316
      static_assert(sizeof(*in) == 1, "Input iterator to 8 bit data required");
317
      if (is_digit(char(*in)))
318
      {
319
        std::string len_s = read_until(in, end, ':', err);
320
        if (err)
321
        {
322
#if TORRENT_USE_ASSERTS
323
          ret.m_type_queried = false;
324
#endif
325
          return;
326
        }
327
        TORRENT_ASSERT(*in == ':');
328
        ++in; // ':'
329
        int len = atoi(len_s.c_str());
330
        ret = entry(entry::string_t);
331
        read_string(in, end, len, ret.string(), err);
332
        if (err)
333
        {
334
#if TORRENT_USE_ASSERTS
335
          ret.m_type_queried = false;
336
#endif
337
          return;
338
        }
339
      }
340
      else
341
      {
342
        err = true;
343
#if TORRENT_USE_ASSERTS
344
        ret.m_type_queried = false;
345
#endif
346
        return;
347
      }
348
#if TORRENT_USE_ASSERTS
349
      ret.m_type_queried = false;
350
#endif
351
    }
352
  }
353
#endif // TORRENT_ABI_VERSION
354
}
355
356
  // This function will encode data to bencoded form.
357
  //
358
  // The entry_ class is the internal representation of the bencoded data
359
  // and it can be used to retrieve information, an entry_ can also be build by
360
  // the program and given to ``bencode()`` to encode it into the ``OutIt``
361
  // iterator.
362
  //
363
  // ``OutIt`` is an OutputIterator_. It's a template and usually
364
  // instantiated as ostream_iterator_ or back_insert_iterator_. This
365
  // function assumes the value_type of the iterator is a ``char``.
366
  // In order to encode entry ``e`` into a buffer, do::
367
  //
368
  //  std::vector<char> buf;
369
  //  bencode(std::back_inserter(buf), e);
370
  //
371
  // .. _OutputIterator:  https://en.cppreference.com/w/cpp/named_req/OutputIterator
372
  // .. _ostream_iterator: https://en.cppreference.com/w/cpp/iterator/ostream_iterator
373
  // .. _back_insert_iterator: https://en.cppreference.com/w/cpp/iterator/back_insert_iterator
374
  template<class OutIt> int bencode(OutIt out, const entry& e)
375
5.01k
  {
376
5.01k
    return aux::bencode_recursive(out, e);
377
5.01k
  }
int libtorrent::bencode<std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > > >(std::__1::back_insert_iterator<std::__1::vector<char, std::__1::allocator<char> > >, libtorrent::entry const&)
Line
Count
Source
375
5.01k
  {
376
5.01k
    return aux::bencode_recursive(out, e);
377
5.01k
  }
Unexecuted instantiation: int libtorrent::bencode<std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > > >(std::__1::back_insert_iterator<libtorrent::aux::noexcept_movable<std::__1::vector<char, std::__1::allocator<char> > > >, libtorrent::entry const&)
Unexecuted instantiation: int libtorrent::bencode<char*>(char*, libtorrent::entry const&)
Unexecuted instantiation: int libtorrent::bencode<std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >(std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, libtorrent::entry const&)
378
379
#if TORRENT_ABI_VERSION == 1
380
  template<class InIt>
381
  TORRENT_DEPRECATED
382
  entry bdecode(InIt start, InIt end)
383
  {
384
    entry e;
385
    bool err = false;
386
    aux::bdecode_recursive(start, end, e, err, 0);
387
    TORRENT_ASSERT(e.m_type_queried == false);
388
    if (err) return entry();
389
    return e;
390
  }
391
  template<class InIt>
392
  TORRENT_DEPRECATED
393
  entry bdecode(InIt start, InIt end
394
    , typename std::iterator_traits<InIt>::difference_type& len)
395
  {
396
    entry e;
397
    bool err = false;
398
    InIt s = start;
399
    aux::bdecode_recursive(start, end, e, err, 0);
400
    len = std::distance(s, start);
401
    TORRENT_ASSERT(len >= 0);
402
    if (err) return entry();
403
    return e;
404
  }
405
#endif
406
}
407
408
#endif // TORRENT_BENCODE_HPP_INCLUDED