Coverage Report

Created: 2025-08-29 06:38

/src/libtorrent/src/natpmp.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
3
Copyright (c) 2007-2010, 2012, 2015-2020, 2022, Arvid Norberg
4
Copyright (c) 2016-2017, 2019, Alden Torres
5
Copyright (c) 2017, Pavel Pimenov
6
Copyright (c) 2018, Steven Siloti
7
All rights reserved.
8
9
Redistribution and use in source and binary forms, with or without
10
modification, are permitted provided that the following conditions
11
are met:
12
13
    * Redistributions of source code must retain the above copyright
14
      notice, this list of conditions and the following disclaimer.
15
    * Redistributions in binary form must reproduce the above copyright
16
      notice, this list of conditions and the following disclaimer in
17
      the documentation and/or other materials provided with the distribution.
18
    * Neither the name of the author nor the names of its
19
      contributors may be used to endorse or promote products derived
20
      from this software without specific prior written permission.
21
22
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
POSSIBILITY OF SUCH DAMAGE.
33
34
*/
35
#include "libtorrent/config.hpp"
36
37
#include "libtorrent/aux_/disable_warnings_push.hpp"
38
39
#if defined TORRENT_OS2
40
#include <pthread.h>
41
#endif
42
43
#include <boost/asio/ip/host_name.hpp>
44
45
#include "libtorrent/aux_/disable_warnings_pop.hpp"
46
47
#include <cstdio> // for snprintf
48
#include <cinttypes> // for PRId64 et.al.
49
#include <cstdarg>
50
#include <functional>
51
#include <cstring> // for memcpy
52
53
#include "libtorrent/natpmp.hpp"
54
#include "libtorrent/io.hpp"
55
#include "libtorrent/assert.hpp"
56
#include "libtorrent/enum_net.hpp"
57
#include "libtorrent/socket_io.hpp"
58
#include "libtorrent/io_context.hpp"
59
#include "libtorrent/aux_/time.hpp"
60
#include "libtorrent/debug.hpp"
61
#include "libtorrent/random.hpp"
62
#include "libtorrent/aux_/ip_helpers.hpp" // for is_local
63
#include "libtorrent/aux_/escape_string.hpp"
64
#include "libtorrent/aux_/numeric_cast.hpp"
65
66
namespace libtorrent {
67
68
struct pcp_error_category final : boost::system::error_category
69
{
70
  const char* name() const BOOST_SYSTEM_NOEXCEPT override
71
0
  { return "pcp error"; }
72
  std::string message(int ev) const override
73
0
  {
74
0
    static char const* msgs[] =
75
0
    {
76
0
      "success",
77
0
      "unsupported version",
78
0
      "not authorized",
79
0
      "malformed request",
80
0
      "unsupported opcode",
81
0
      "unsupported option",
82
0
      "malformed option",
83
0
      "network failure",
84
0
      "no resources",
85
0
      "unsupported protocol",
86
0
      "user exceeded quota",
87
0
      "cannot provide external",
88
0
      "address mismatch",
89
0
      "excessive remote peers",
90
0
    };
91
0
    if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0])))
92
0
      return "Unknown error";
93
0
    return msgs[ev];
94
0
  }
95
  boost::system::error_condition default_error_condition(
96
    int ev) const BOOST_SYSTEM_NOEXCEPT override
97
0
  { return {ev, *this}; }
98
};
99
100
boost::system::error_category& pcp_category()
101
0
{
102
0
  static pcp_error_category pcp_category;
103
0
  return pcp_category;
104
0
}
105
106
namespace errors
107
{
108
  // hidden
109
  boost::system::error_code make_error_code(pcp_errors e)
110
0
  {
111
0
    return {e, pcp_category()};
112
0
  }
113
}
114
115
error_code natpmp::from_result_code(int const version, int result)
116
0
{
117
0
  if (version == version_natpmp)
118
0
  {
119
    // a few nat-pmp result codes map to different codes
120
    // in pcp
121
0
    switch (result)
122
0
    {
123
0
    case 3:result = 7; break;
124
0
    case 4:result = 8; break;
125
0
    case 5:result = 4; break;
126
0
    }
127
0
  }
128
0
  return errors::pcp_errors(result);
129
0
}
130
131
char const* natpmp::version_to_string(protocol_version version)
132
0
{
133
0
  return version == version_natpmp ? "NAT-PMP" : "PCP";
134
0
}
135
136
using namespace aux;
137
using namespace std::placeholders;
138
139
natpmp::natpmp(io_context& ios
140
  , aux::portmap_callback& cb
141
  , listen_socket_handle ls)
142
0
  : m_callback(cb)
143
0
  , m_socket(ios)
144
0
  , m_send_timer(ios)
145
0
  , m_refresh_timer(ios)
146
0
  , m_ioc(ios)
147
0
  , m_listen_handle(std::move(ls))
148
0
{
149
  // unfortunately async operations rely on the storage
150
  // for this array not to be reallocated, by passing
151
  // around pointers to its elements. so reserve size for now
152
0
  m_mappings.reserve(10);
153
0
}
154
155
void natpmp::start(ip_interface const& ip)
156
0
{
157
0
  TORRENT_ASSERT(is_single_thread());
158
159
  // assume servers support PCP and fall back to NAT-PMP
160
  // if necessary
161
0
  m_version = version_pcp;
162
163
0
  address const& local_address = ip.interface_address;
164
165
0
  error_code ec;
166
0
  auto const routes = enum_routes(m_ioc, ec);
167
0
  if (ec)
168
0
  {
169
#ifndef TORRENT_DISABLE_LOGGING
170
    if (should_log())
171
    {
172
      log("failed to enumerate routes: %s"
173
        , convert_from_native(ec.message()).c_str());
174
    }
175
#endif
176
0
    disable(ec);
177
0
  }
178
179
0
  auto const route = get_gateway(ip, routes);
180
181
0
  if (!route)
182
0
  {
183
#ifndef TORRENT_DISABLE_LOGGING
184
    if (should_log())
185
    {
186
      log("failed to find default route for \"%s\" %s"
187
        , ip.name, local_address.to_string().c_str());
188
    }
189
#endif
190
0
    disable(ec);
191
0
    return;
192
0
  }
193
194
0
  m_disabled = false;
195
196
0
  udp::endpoint const nat_endpoint(*route, 5351);
197
0
  if (nat_endpoint == m_nat_endpoint) return;
198
0
  m_nat_endpoint = nat_endpoint;
199
200
#ifndef TORRENT_DISABLE_LOGGING
201
  if (should_log())
202
  {
203
    log("found gateway at: %s"
204
      , print_address(m_nat_endpoint.address()).c_str());
205
  }
206
#endif
207
208
0
  m_socket.open(local_address.is_v4() ? udp::v4() : udp::v6(), ec);
209
0
  if (ec)
210
0
  {
211
0
    disable(ec);
212
0
    return;
213
0
  }
214
0
  m_socket.bind({local_address, 0}, ec);
215
0
  if (ec)
216
0
  {
217
0
    disable(ec);
218
0
    return;
219
0
  }
220
221
0
  ADD_OUTSTANDING_ASYNC("natpmp::on_reply");
222
0
  m_socket.async_receive_from(boost::asio::buffer(&m_response_buffer[0]
223
0
    , sizeof(m_response_buffer))
224
0
    , m_remote, std::bind(&natpmp::on_reply, self(), _1, _2));
225
0
  if (m_version == version_natpmp)
226
0
    send_get_ip_address_request();
227
228
0
  for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
229
0
  {
230
0
    if (i->protocol == portmap_protocol::none
231
0
      || i->act != portmap_action::none)
232
0
      continue;
233
0
    i->act = portmap_action::add;
234
0
    update_mapping(port_mapping_t(int(i - m_mappings.begin())));
235
0
  }
236
0
}
237
238
void natpmp::send_get_ip_address_request()
239
0
{
240
0
  TORRENT_ASSERT(is_single_thread());
241
0
  using namespace libtorrent::aux;
242
243
  // this opcode only exists in NAT-PMP
244
  // PCP routers report the external IP in the response to a MAP operation
245
0
  TORRENT_ASSERT(m_version == version_natpmp);
246
0
  if (m_version != version_natpmp)
247
0
    return;
248
249
0
  char buf[2];
250
0
  char* out = buf;
251
0
  write_uint8(version_natpmp, out);
252
0
  write_uint8(0, out); // public IP address request opcode
253
#ifndef TORRENT_DISABLE_LOGGING
254
  log("==> get public IP address");
255
#endif
256
257
0
  error_code ec;
258
0
  m_socket.send_to(boost::asio::buffer(buf, sizeof(buf)), m_nat_endpoint, 0, ec);
259
0
}
260
261
bool natpmp::get_mapping(port_mapping_t const index, int& local_port
262
  , int& external_port, portmap_protocol& protocol) const
263
0
{
264
0
  TORRENT_ASSERT(is_single_thread());
265
266
0
  TORRENT_ASSERT(index < m_mappings.end_index() && index >= port_mapping_t{});
267
0
  if (index >= m_mappings.end_index() || index < port_mapping_t{}) return false;
268
0
  mapping_t const& m = m_mappings[index];
269
0
  if (m.protocol == portmap_protocol::none) return false;
270
0
  local_port = m.local_port;
271
0
  external_port = m.external_port;
272
0
  protocol = m.protocol;
273
0
  return true;
274
0
}
275
276
#ifndef TORRENT_DISABLE_LOGGING
277
bool natpmp::should_log() const
278
{
279
  return m_callback.should_log_portmap(portmap_transport::natpmp);
280
}
281
282
void natpmp::mapping_log(char const* op, mapping_t const& m) const
283
{
284
  if (should_log())
285
  {
286
    log("%s-mapping: proto: %s port: %d local-port: %d action: %s ttl: %" PRId64
287
      , op
288
      , m.protocol == portmap_protocol::none
289
      ? "none" : to_string(m.protocol)
290
      , m.external_port
291
      , m.local_port
292
      , to_string(m.act)
293
      , (m.expires.time_since_epoch() != seconds(0))
294
        ? total_seconds(m.expires - aux::time_now())
295
        : std::int64_t(0));
296
  }
297
}
298
299
TORRENT_FORMAT(2, 3)
300
void natpmp::log(char const* fmt, ...) const
301
{
302
  TORRENT_ASSERT(is_single_thread());
303
  if (!should_log()) return;
304
  char msg[200];
305
  va_list v;
306
  va_start(v, fmt);
307
  std::vsnprintf(msg, sizeof(msg), fmt, v);
308
  va_end(v);
309
  m_callback.log_portmap(portmap_transport::natpmp, msg, m_listen_handle);
310
}
311
#endif
312
313
void natpmp::disable(error_code const& ec)
314
0
{
315
0
  TORRENT_ASSERT(is_single_thread());
316
0
  m_disabled = true;
317
318
0
  for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
319
0
  {
320
0
    if (i->protocol == portmap_protocol::none) continue;
321
0
    portmap_protocol const proto = i->protocol;
322
0
    i->protocol = portmap_protocol::none;
323
0
    port_mapping_t const index(static_cast<int>(i - m_mappings.begin()));
324
0
    m_callback.on_port_mapping(index, address(), 0, proto, ec
325
0
      , portmap_transport::natpmp, m_listen_handle);
326
0
  }
327
0
  close_impl();
328
0
}
329
330
void natpmp::delete_mapping(port_mapping_t const index)
331
0
{
332
0
  TORRENT_ASSERT(is_single_thread());
333
334
0
  TORRENT_ASSERT(index < m_mappings.end_index() && index >= port_mapping_t{});
335
0
  if (index >= m_mappings.end_index() || index < port_mapping_t{}) return;
336
0
  mapping_t& m = m_mappings[index];
337
338
0
  if (m.protocol == portmap_protocol::none) return;
339
0
  if (!m.map_sent)
340
0
  {
341
0
    m.act = portmap_action::none;
342
0
    m.protocol = portmap_protocol::none;
343
0
    return;
344
0
  }
345
346
0
  m.act = portmap_action::del;
347
0
  update_mapping(index);
348
0
}
349
350
port_mapping_t natpmp::add_mapping(portmap_protocol const p, int const external_port
351
  , tcp::endpoint const local_ep, std::string const&)
352
0
{
353
0
  TORRENT_ASSERT(is_single_thread());
354
355
0
  if (m_disabled) return port_mapping_t{-1};
356
357
0
  auto i = std::find_if(m_mappings.begin()
358
0
    , m_mappings.end(), [] (mapping_t const& m) { return m.protocol == portmap_protocol::none; });
359
0
  if (i == m_mappings.end())
360
0
  {
361
0
    m_mappings.push_back(mapping_t());
362
0
    i = m_mappings.end() - 1;
363
0
  }
364
0
  aux::crypto_random_bytes(i->nonce);
365
0
  i->protocol = p;
366
0
  i->external_port = external_port;
367
0
  i->local_port = local_ep.port();
368
0
  i->act = portmap_action::add;
369
370
0
  port_mapping_t const mapping_index(static_cast<int>(i - m_mappings.begin()));
371
#ifndef TORRENT_DISABLE_LOGGING
372
  mapping_log("add",*i);
373
#endif
374
375
0
  update_mapping(mapping_index);
376
0
  return port_mapping_t{mapping_index};
377
0
}
378
379
void natpmp::try_next_mapping(port_mapping_t const i)
380
0
{
381
0
  TORRENT_ASSERT(is_single_thread());
382
0
  if (i < prev(m_mappings.end_index()))
383
0
  {
384
0
    update_mapping(next(i));
385
0
    return;
386
0
  }
387
388
0
  auto const m = std::find_if(
389
0
    m_mappings.begin(), m_mappings.end()
390
0
    , [] (mapping_t const& ma) { return ma.act != portmap_action::none
391
0
      && ma.protocol != portmap_protocol::none; });
392
393
0
  if (m == m_mappings.end())
394
0
  {
395
0
    if (m_abort)
396
0
    {
397
0
      m_send_timer.cancel();
398
0
      error_code ec;
399
0
      m_socket.close(ec);
400
0
    }
401
0
    return;
402
0
  }
403
404
0
  update_mapping(port_mapping_t(static_cast<int>(m - m_mappings.begin())));
405
0
}
406
407
void natpmp::update_mapping(port_mapping_t const i)
408
0
{
409
0
  TORRENT_ASSERT(is_single_thread());
410
0
  if (i == m_mappings.end_index())
411
0
  {
412
0
    if (m_abort)
413
0
    {
414
0
      m_send_timer.cancel();
415
0
      error_code ec;
416
0
      m_socket.close(ec);
417
0
    }
418
0
    return;
419
0
  }
420
421
0
  mapping_t const& m = m_mappings[i];
422
423
#ifndef TORRENT_DISABLE_LOGGING
424
  mapping_log("update", m);
425
#endif
426
427
0
  if (m.act == portmap_action::none
428
0
    || m.protocol == portmap_protocol::none)
429
0
  {
430
0
    try_next_mapping(i);
431
0
    return;
432
0
  }
433
434
0
  if (m_currently_mapping == port_mapping_t{-1})
435
0
  {
436
    // the socket is not currently in use
437
    // send out a mapping request
438
0
    m_retry_count = 0;
439
0
    send_map_request(i);
440
0
  }
441
0
}
442
443
void natpmp::send_map_request(port_mapping_t const i)
444
0
{
445
0
  TORRENT_ASSERT(is_single_thread());
446
0
  using namespace libtorrent::aux;
447
448
0
  TORRENT_ASSERT(m_currently_mapping == port_mapping_t{-1}
449
0
    || m_currently_mapping == i);
450
0
  m_currently_mapping = i;
451
0
  mapping_t& m = m_mappings[i];
452
0
  TORRENT_ASSERT(m.act != portmap_action::none);
453
0
  char buf[60];
454
0
  char* out = buf;
455
0
  int ttl = m.act == portmap_action::add ? 3600 : 0;
456
0
  if (m_version == version_natpmp)
457
0
  {
458
0
    write_uint8(m_version, out);
459
0
    write_uint8(m.protocol == portmap_protocol::udp ? 1 : 2, out); // map "protocol"
460
0
    write_uint16(0, out); // reserved
461
0
    write_uint16(m.local_port, out); // private port
462
0
    write_uint16(m.external_port, out); // requested public port
463
0
    write_uint32(ttl, out); // port mapping lifetime
464
0
  }
465
0
  else if (m_version == version_pcp)
466
0
  {
467
    // PCP requires the use of IPv6 addresses even for IPv4 messages
468
0
    write_uint8(m_version, out);
469
0
    write_uint8(opcode_map, out);
470
0
    write_uint16(0, out); // reserved
471
0
    write_uint32(ttl, out);
472
0
    error_code ec;
473
0
    address const local_addr = m_socket.local_endpoint(ec).address();
474
0
    if (ec)
475
0
    {
476
#ifndef TORRENT_DISABLE_LOGGING
477
      if ( should_log())
478
      {
479
        log("*** port map, local_endpoint [ ec: %s:%d %s ]"
480
          , ec.category().name()
481
          , ec.value()
482
          , ec.message().c_str());
483
      }
484
#endif
485
0
      m_currently_mapping = port_mapping_t{-1};
486
0
      m.act = portmap_action::none;
487
0
      return;
488
0
    }
489
0
    auto const local_bytes = local_addr.is_v4()
490
0
      ? make_address_v6(v4_mapped, local_addr.to_v4()).to_bytes()
491
0
      : local_addr.to_v6().to_bytes();
492
0
    out = std::copy(local_bytes.begin(), local_bytes.end(), out);
493
0
    out = std::copy(m.nonce.begin(), m.nonce.end(), out);
494
    // translate portmap_protocol to an IANA protocol number
495
0
    int const protocol =
496
0
      (m.protocol == portmap_protocol::tcp) ? 6
497
0
      : (m.protocol == portmap_protocol::udp) ? 17
498
0
      : 0;
499
0
    write_int8(protocol, out);
500
0
    write_uint8(0, out); // reserved
501
0
    write_uint16(0, out); // reserved
502
0
    write_uint16(m.local_port, out);
503
0
    write_uint16(m.external_port, out);
504
0
    address_v6 external_addr;
505
0
    if (!m.external_address.is_unspecified())
506
0
    {
507
0
      external_addr = m.external_address.is_v4()
508
0
        ? make_address_v6(v4_mapped, m.external_address.to_v4())
509
0
        : m.external_address.to_v6();
510
0
    }
511
0
    else if (aux::is_local(local_addr))
512
0
    {
513
0
      external_addr = local_addr.is_v4()
514
0
        ? make_address_v6(v4_mapped, address_v4())
515
0
        : address_v6();
516
0
    }
517
0
    else if (local_addr.is_v4())
518
0
    {
519
0
      external_addr = make_address_v6(v4_mapped, local_addr.to_v4());
520
0
    }
521
0
    else
522
0
    {
523
0
      external_addr = local_addr.to_v6();
524
0
    }
525
0
    write_address(external_addr, out);
526
0
  }
527
0
  else
528
0
  {
529
0
    TORRENT_ASSERT_FAIL();
530
0
  }
531
532
#ifndef TORRENT_DISABLE_LOGGING
533
  if (should_log())
534
  {
535
    log("==> port map [ mapping: %d action: %s"
536
      " transport: %s proto: %s local: %d external: %d ttl: %d ]"
537
      , static_cast<int>(i), to_string(m.act)
538
      , version_to_string(m_version)
539
      , to_string(m.protocol)
540
      , m.local_port, m.external_port, ttl);
541
  }
542
#endif
543
544
0
  error_code ec;
545
0
  m_socket.send_to(boost::asio::buffer(buf, std::size_t(out - buf)), m_nat_endpoint, 0, ec);
546
#ifndef TORRENT_DISABLE_LOGGING
547
  if (ec && should_log())
548
  {
549
    log("*** port map [ ec: %s:%d %s ]"
550
      , ec.category().name()
551
      , ec.value()
552
      , ec.message().c_str());
553
  }
554
#endif
555
0
  m.map_sent = true;
556
0
  m.outstanding_request = true;
557
558
0
  if (m_abort)
559
0
  {
560
    // when we're shutting down, ignore the
561
    // responses and just remove all mappings
562
    // immediately
563
0
    m_currently_mapping = port_mapping_t{-1};
564
0
    m.act = portmap_action::none;
565
0
    try_next_mapping(i);
566
0
  }
567
0
  else
568
0
  {
569
0
    ADD_OUTSTANDING_ASYNC("natpmp::resend_request");
570
    // linear back-off instead of exponential
571
0
    ++m_retry_count;
572
0
    m_send_timer.expires_after(milliseconds(250 * m_retry_count));
573
0
    m_send_timer.async_wait(std::bind(&natpmp::on_resend_request, self(), i, _1));
574
0
  }
575
0
}
576
577
void natpmp::on_resend_request(port_mapping_t const i, error_code const& e)
578
0
{
579
0
  TORRENT_ASSERT(is_single_thread());
580
0
  COMPLETE_ASYNC("natpmp::resend_request");
581
0
  if (e) return;
582
0
  resend_request(i);
583
0
}
584
585
void natpmp::resend_request(port_mapping_t const i)
586
0
{
587
0
  if (m_currently_mapping != i) return;
588
589
  // if we're shutting down, don't retry, just move on
590
  // to the next mapping
591
0
  if (m_retry_count >= 9 || m_abort)
592
0
  {
593
0
    m_currently_mapping = port_mapping_t{-1};
594
0
    m_mappings[i].act = portmap_action::none;
595
    // try again in two hours
596
0
    m_mappings[i].expires = aux::time_now() + hours(2);
597
0
    try_next_mapping(i);
598
0
    return;
599
0
  }
600
0
  send_map_request(i);
601
0
}
602
603
void natpmp::on_reply(error_code const& e
604
  , std::size_t const bytes_transferred)
605
0
{
606
0
  TORRENT_ASSERT(is_single_thread());
607
608
0
  COMPLETE_ASYNC("natpmp::on_reply");
609
610
0
  using namespace libtorrent::aux;
611
0
  if (e)
612
0
  {
613
#ifndef TORRENT_DISABLE_LOGGING
614
    if (should_log())
615
    {
616
      log("error on receiving reply: %s"
617
        , convert_from_native(e.message()).c_str());
618
    }
619
#endif
620
0
    return;
621
0
  }
622
623
0
  if (m_abort) return;
624
625
0
  ADD_OUTSTANDING_ASYNC("natpmp::on_reply");
626
  // make a copy of the response packet buffer
627
  // to avoid overwriting it in the next receive call
628
0
  std::array<char, sizeof(m_response_buffer)> msg_buf;
629
0
  std::memcpy(msg_buf.data(), m_response_buffer, bytes_transferred);
630
631
0
  m_socket.async_receive_from(boost::asio::buffer(&m_response_buffer[0]
632
0
    , sizeof(m_response_buffer))
633
0
    , m_remote, std::bind(&natpmp::on_reply, self(), _1, _2));
634
635
0
  if (m_remote != m_nat_endpoint)
636
0
  {
637
#ifndef TORRENT_DISABLE_LOGGING
638
    if (should_log())
639
    {
640
      log("received packet from wrong IP: %s"
641
        , print_endpoint(m_remote).c_str());
642
    }
643
#endif
644
0
    return;
645
0
  }
646
647
0
  m_send_timer.cancel();
648
649
0
  if (bytes_transferred < 4)
650
0
  {
651
#ifndef TORRENT_DISABLE_LOGGING
652
    log("received packet of invalid size: %d", int(bytes_transferred));
653
#endif
654
0
    return;
655
0
  }
656
657
0
  char* in = msg_buf.data();
658
0
  int const version = read_uint8(in);
659
660
0
  if (version != version_natpmp && version != version_pcp)
661
0
  {
662
#ifndef TORRENT_DISABLE_LOGGING
663
    log("unexpected version: %d", version);
664
#endif
665
0
    return;
666
0
  }
667
668
0
  int cmd = read_uint8(in);
669
0
  if (version == version_pcp)
670
0
  {
671
0
    cmd &= 0x7f;
672
0
  }
673
0
  int result;
674
0
  if (version == version_pcp)
675
0
  {
676
0
    ++in; // reserved
677
0
    result = read_uint8(in);
678
0
  }
679
0
  else
680
0
  {
681
0
    result = read_uint16(in);
682
0
  }
683
684
0
  if (result == errors::pcp_unsupp_version)
685
0
  {
686
#ifndef TORRENT_DISABLE_LOGGING
687
    log("unsupported version");
688
#endif
689
    // ignore errors from local_endpoint
690
0
    error_code ec;
691
0
    if (m_version == version_pcp && !aux::is_v6(m_socket.local_endpoint(ec)))
692
0
    {
693
0
      m_version = version_natpmp;
694
0
      resend_request(m_currently_mapping);
695
0
      send_get_ip_address_request();
696
0
    }
697
0
    return;
698
0
  }
699
700
0
  if ((version == version_natpmp && bytes_transferred < 12)
701
0
    || (version == version_pcp && bytes_transferred < 24))
702
0
  {
703
#ifndef TORRENT_DISABLE_LOGGING
704
    log("received packet of invalid size: %d", int(bytes_transferred));
705
#endif
706
0
    return;
707
0
  }
708
709
0
  int lifetime = 0;
710
0
  if (version == version_pcp)
711
0
  {
712
0
    lifetime = aux::numeric_cast<int>(read_uint32(in));
713
0
  }
714
0
  int const time = aux::numeric_cast<int>(read_uint32(in));
715
0
  if (version == version_pcp) in += 12; // reserved
716
0
  TORRENT_UNUSED(time);
717
718
0
  if (version == version_natpmp && cmd == 128)
719
0
  {
720
    // public IP request response
721
0
    m_external_ip = read_v4_address(in);
722
723
#ifndef TORRENT_DISABLE_LOGGING
724
    if (should_log())
725
    {
726
      log("<== public IP address [ %s ]", print_address(m_external_ip).c_str());
727
    }
728
#endif
729
0
    return;
730
731
0
  }
732
733
0
  if ((version == version_natpmp && bytes_transferred != 16)
734
0
    || (version == version_pcp && bytes_transferred != 60))
735
0
  {
736
#ifndef TORRENT_DISABLE_LOGGING
737
    log("received packet of invalid size: %d", int(bytes_transferred));
738
#endif
739
0
    return;
740
0
  }
741
742
0
  std::array<char, 12> nonce;
743
0
  portmap_protocol protocol = portmap_protocol::none;
744
0
  if (version == version_pcp)
745
0
  {
746
0
    std::memcpy(nonce.data(), in, nonce.size());
747
0
    in += nonce.size();
748
0
    int p = read_uint8(in);
749
0
    protocol = p == 6 ? portmap_protocol::tcp
750
0
      : portmap_protocol::udp;
751
0
    in += 3; // reserved
752
0
  }
753
0
  int const private_port = read_uint16(in);
754
0
  int const public_port = read_uint16(in);
755
0
  if (version == version_natpmp)
756
0
    lifetime = aux::numeric_cast<int>(read_uint32(in));
757
0
  address external_addr;
758
0
  if (version == version_pcp)
759
0
  {
760
0
    external_addr = read_v6_address(in);
761
0
    if (external_addr.to_v6().is_v4_mapped())
762
0
      external_addr = make_address_v4(v4_mapped, external_addr.to_v6());
763
0
  }
764
765
0
  if (version == version_natpmp)
766
0
  {
767
0
    protocol = (cmd - 128 == 1)
768
0
      ? portmap_protocol::udp
769
0
      : portmap_protocol::tcp;
770
0
  }
771
772
#ifndef TORRENT_DISABLE_LOGGING
773
  char msg[200];
774
  int const num_chars = std::snprintf(msg, sizeof(msg), "<== port map ["
775
    " transport: %s protocol: %s local: %d external: %d ttl: %d ]"
776
    , version_to_string(protocol_version(version))
777
    , (protocol == portmap_protocol::udp ? "udp" : "tcp")
778
    , private_port, public_port, lifetime);
779
#endif
780
781
0
  mapping_t* m = nullptr;
782
0
  port_mapping_t index{-1};
783
0
  for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
784
0
  {
785
0
    if (private_port != i->local_port) continue;
786
0
    if (protocol != i->protocol) continue;
787
0
    if (!i->map_sent) continue;
788
0
    if (!i->outstanding_request) continue;
789
0
    if (version == version_pcp && nonce != i->nonce) continue;
790
0
    m = &*i;
791
0
    index = port_mapping_t(static_cast<int>(i - m_mappings.begin()));
792
0
    break;
793
0
  }
794
795
0
  if (m == nullptr)
796
0
  {
797
#ifndef TORRENT_DISABLE_LOGGING
798
    snprintf(msg + num_chars, sizeof(msg) - aux::numeric_cast<std::size_t>(num_chars), " not found in map table");
799
    log("%s", msg);
800
#endif
801
0
    return;
802
0
  }
803
0
  m->outstanding_request = false;
804
805
#ifndef TORRENT_DISABLE_LOGGING
806
  log("%s", msg);
807
#endif
808
809
0
  if (public_port == 0 || lifetime == 0)
810
0
  {
811
    // this means the mapping was
812
    // successfully closed
813
0
    m->protocol = portmap_protocol::none;
814
0
  }
815
0
  else
816
0
  {
817
0
    m->expires = aux::time_now() + seconds(lifetime * 3 / 4);
818
0
    m->external_port = public_port;
819
0
    if (!external_addr.is_unspecified())
820
0
      m->external_address = external_addr;
821
0
  }
822
823
0
  if (result != 0)
824
0
  {
825
0
    m->expires = aux::time_now() + hours(2);
826
0
    portmap_protocol const proto = m->protocol;
827
0
    m_callback.on_port_mapping(port_mapping_t{index}, address(), 0, proto
828
0
      , from_result_code(version, result), portmap_transport::natpmp, m_listen_handle);
829
0
  }
830
0
  else if (m->act == portmap_action::add)
831
0
  {
832
0
    portmap_protocol const proto = m->protocol;
833
0
    address const ext_ip = version == version_pcp ? m->external_address : m_external_ip;
834
0
    m_callback.on_port_mapping(port_mapping_t{index}, ext_ip, m->external_port, proto
835
0
      , errors::pcp_success, portmap_transport::natpmp, m_listen_handle);
836
0
  }
837
838
0
  m_currently_mapping = port_mapping_t{-1};
839
0
  m->act = portmap_action::none;
840
0
  m_send_timer.cancel();
841
0
  update_expiration_timer();
842
0
  try_next_mapping(index);
843
0
}
844
845
void natpmp::update_expiration_timer()
846
0
{
847
0
  TORRENT_ASSERT(is_single_thread());
848
0
  if (m_abort) return;
849
850
0
  time_point const now = aux::time_now() + milliseconds(100);
851
0
  time_point min_expire = now + seconds(3600);
852
0
  port_mapping_t min_index{-1};
853
0
  for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
854
0
  {
855
0
    if (i->protocol == portmap_protocol::none
856
0
      || i->act != portmap_action::none) continue;
857
0
    port_mapping_t const index(static_cast<int>(i - m_mappings.begin()));
858
0
    if (i->expires < now)
859
0
    {
860
#ifndef TORRENT_DISABLE_LOGGING
861
      log("mapping %d expired", static_cast<int>(index));
862
#endif
863
0
      i->act = portmap_action::add;
864
0
      if (m_next_refresh == index) m_next_refresh = port_mapping_t{-1};
865
0
      update_mapping(index);
866
0
    }
867
0
    else if (i->expires < min_expire)
868
0
    {
869
0
      min_expire = i->expires;
870
0
      min_index = index;
871
0
    }
872
0
  }
873
874
  // this is already the mapping we're waiting for
875
0
  if (m_next_refresh == min_index) return;
876
877
0
  if (min_index >= port_mapping_t{})
878
0
  {
879
#ifndef TORRENT_DISABLE_LOGGING
880
    log("next expiration [ idx: %d ttl: %" PRId64 " ]"
881
      , static_cast<int>(min_index), total_seconds(min_expire - aux::time_now()));
882
#endif
883
0
    if (m_next_refresh >= port_mapping_t{}) m_refresh_timer.cancel();
884
885
0
    ADD_OUTSTANDING_ASYNC("natpmp::mapping_expired");
886
0
    m_refresh_timer.expires_after(min_expire - now);
887
0
    m_refresh_timer.async_wait(std::bind(&natpmp::mapping_expired, self(), _1, min_index));
888
0
    m_next_refresh = min_index;
889
0
  }
890
0
}
891
892
void natpmp::mapping_expired(error_code const& e, port_mapping_t const i)
893
0
{
894
0
  TORRENT_ASSERT(is_single_thread());
895
0
  COMPLETE_ASYNC("natpmp::mapping_expired");
896
0
  if (e || m_abort) return;
897
#ifndef TORRENT_DISABLE_LOGGING
898
  log("mapping %d expired", static_cast<int>(i));
899
#endif
900
0
  m_mappings[i].act = portmap_action::add;
901
0
  if (m_next_refresh == i) m_next_refresh = port_mapping_t{-1};
902
0
  update_mapping(i);
903
0
}
904
905
void natpmp::close()
906
0
{
907
0
  TORRENT_ASSERT(is_single_thread());
908
0
  close_impl();
909
0
}
910
911
void natpmp::close_impl()
912
0
{
913
0
  TORRENT_ASSERT(is_single_thread());
914
0
  m_abort = true;
915
#ifndef TORRENT_DISABLE_LOGGING
916
  log("closing");
917
#endif
918
0
  if (m_disabled) return;
919
0
  for (auto& m : m_mappings)
920
0
  {
921
0
    if (m.protocol == portmap_protocol::none) continue;
922
0
    m.act = portmap_action::del;
923
0
  }
924
0
  m_refresh_timer.cancel();
925
0
  m_currently_mapping = port_mapping_t{-1};
926
0
  update_mapping(port_mapping_t{});
927
0
}
928
929
} // namespace libtorrent