PageRenderTime 58ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/Import/libtorrent/src/kademlia/rpc_manager.cpp

https://gitlab.com/andreicristianpetcu/popcorn-time
C++ | 522 lines | 381 code | 79 blank | 62 comment | 61 complexity | c7d1a55bb1f66d878bf2c78eac7f6867 MD5 | raw file
  1. /*
  2. Copyright (c) 2006-2014, Arvid Norberg
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions
  6. are met:
  7. * Redistributions of source code must retain the above copyright
  8. notice, this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright
  10. notice, this list of conditions and the following disclaimer in
  11. the documentation and/or other materials provided with the distribution.
  12. * Neither the name of the author nor the names of its
  13. contributors may be used to endorse or promote products derived
  14. from this software without specific prior written permission.
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  19. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  20. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  21. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  22. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  23. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  25. POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. #include "libtorrent/socket.hpp"
  28. #include <boost/bind.hpp>
  29. #include <libtorrent/io.hpp>
  30. #include <libtorrent/random.hpp>
  31. #include <libtorrent/invariant_check.hpp>
  32. #include <libtorrent/kademlia/node_id.hpp> // for generate_random_id
  33. #include <libtorrent/kademlia/rpc_manager.hpp>
  34. #include <libtorrent/kademlia/logging.hpp>
  35. #include <libtorrent/kademlia/routing_table.hpp>
  36. #include <libtorrent/kademlia/find_data.hpp>
  37. #include <libtorrent/kademlia/refresh.hpp>
  38. #include <libtorrent/kademlia/node.hpp>
  39. #include <libtorrent/kademlia/observer.hpp>
  40. #include <libtorrent/hasher.hpp>
  41. #include <libtorrent/session_settings.hpp> // for dht_settings
  42. #include <libtorrent/time.hpp>
  43. #include <time.h> // time()
  44. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  45. #include <fstream>
  46. #endif
  47. namespace libtorrent { namespace dht
  48. {
  49. namespace io = libtorrent::detail;
  50. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  51. TORRENT_DEFINE_LOG(rpc)
  52. #endif
  53. void intrusive_ptr_add_ref(observer const* o)
  54. {
  55. TORRENT_ASSERT(o != 0);
  56. TORRENT_ASSERT(o->m_refs >= 0);
  57. ++o->m_refs;
  58. }
  59. void intrusive_ptr_release(observer const* o)
  60. {
  61. TORRENT_ASSERT(o != 0);
  62. TORRENT_ASSERT(o->m_refs > 0);
  63. if (--o->m_refs == 0)
  64. {
  65. boost::intrusive_ptr<traversal_algorithm> ta = o->m_algorithm;
  66. (const_cast<observer*>(o))->~observer();
  67. ta->free_observer(const_cast<observer*>(o));
  68. }
  69. }
  70. void observer::set_target(udp::endpoint const& ep)
  71. {
  72. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  73. // use high resolution timers for logging
  74. m_sent = time_now_hires();
  75. #else
  76. m_sent = time_now();
  77. #endif
  78. m_port = ep.port();
  79. #if TORRENT_USE_IPV6
  80. if (ep.address().is_v6())
  81. {
  82. flags |= flag_ipv6_address;
  83. m_addr.v6 = ep.address().to_v6().to_bytes();
  84. }
  85. else
  86. #endif
  87. {
  88. flags &= ~flag_ipv6_address;
  89. m_addr.v4 = ep.address().to_v4().to_bytes();
  90. }
  91. }
  92. address observer::target_addr() const
  93. {
  94. #if TORRENT_USE_IPV6
  95. if (flags & flag_ipv6_address)
  96. return address_v6(m_addr.v6);
  97. else
  98. #endif
  99. return address_v4(m_addr.v4);
  100. }
  101. udp::endpoint observer::target_ep() const
  102. {
  103. return udp::endpoint(target_addr(), m_port);
  104. }
  105. void observer::abort()
  106. {
  107. if (flags & flag_done) return;
  108. flags |= flag_done;
  109. m_algorithm->failed(observer_ptr(this), traversal_algorithm::prevent_request);
  110. }
  111. void observer::done()
  112. {
  113. if (flags & flag_done) return;
  114. flags |= flag_done;
  115. m_algorithm->finished(observer_ptr(this));
  116. }
  117. void observer::short_timeout()
  118. {
  119. if (flags & flag_short_timeout) return;
  120. m_algorithm->failed(observer_ptr(this), traversal_algorithm::short_timeout);
  121. }
  122. // this is called when no reply has been received within
  123. // some timeout
  124. void observer::timeout()
  125. {
  126. if (flags & flag_done) return;
  127. flags |= flag_done;
  128. m_algorithm->failed(observer_ptr(this));
  129. }
  130. void observer::set_id(node_id const& id)
  131. {
  132. if (m_id == id) return;
  133. m_id = id;
  134. if (m_algorithm) m_algorithm->resort_results();
  135. }
  136. enum { observer_size = max3<
  137. sizeof(find_data_observer)
  138. , sizeof(announce_observer)
  139. , sizeof(null_observer)
  140. >::value
  141. };
  142. rpc_manager::rpc_manager(node_id const& our_id
  143. , routing_table& table, udp_socket_interface* sock)
  144. : m_pool_allocator(observer_size, 10)
  145. , m_sock(sock)
  146. , m_table(table)
  147. , m_timer(time_now())
  148. , m_our_id(our_id)
  149. , m_allocated_observers(0)
  150. , m_destructing(false)
  151. {
  152. std::srand((unsigned int)time(0));
  153. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  154. TORRENT_LOG(rpc) << "Constructing";
  155. #define PRINT_OFFSETOF(x, y) TORRENT_LOG(rpc) << " +" << offsetof(x, y) << ": " #y
  156. TORRENT_LOG(rpc) << " observer: " << sizeof(observer);
  157. PRINT_OFFSETOF(dht::observer, m_sent);
  158. PRINT_OFFSETOF(dht::observer, m_refs);
  159. PRINT_OFFSETOF(dht::observer, m_algorithm);
  160. PRINT_OFFSETOF(dht::observer, m_id);
  161. PRINT_OFFSETOF(dht::observer, m_addr);
  162. PRINT_OFFSETOF(dht::observer, m_port);
  163. PRINT_OFFSETOF(dht::observer, m_transaction_id);
  164. PRINT_OFFSETOF(dht::observer, flags);
  165. TORRENT_LOG(rpc) << " announce_observer: " << sizeof(announce_observer);
  166. TORRENT_LOG(rpc) << " null_observer: " << sizeof(null_observer);
  167. TORRENT_LOG(rpc) << " find_data_observer: " << sizeof(find_data_observer);
  168. #undef PRINT_OFFSETOF
  169. #endif
  170. }
  171. rpc_manager::~rpc_manager()
  172. {
  173. TORRENT_ASSERT(!m_destructing);
  174. m_destructing = true;
  175. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  176. TORRENT_LOG(rpc) << "Destructing";
  177. #endif
  178. for (transactions_t::iterator i = m_transactions.begin()
  179. , end(m_transactions.end()); i != end; ++i)
  180. {
  181. (*i)->abort();
  182. }
  183. }
  184. void* rpc_manager::allocate_observer()
  185. {
  186. m_pool_allocator.set_next_size(10);
  187. void* ret = m_pool_allocator.malloc();
  188. if (ret) ++m_allocated_observers;
  189. return ret;
  190. }
  191. void rpc_manager::free_observer(void* ptr)
  192. {
  193. if (!ptr) return;
  194. --m_allocated_observers;
  195. TORRENT_ASSERT(reinterpret_cast<observer*>(ptr)->m_in_use == false);
  196. m_pool_allocator.free(ptr);
  197. }
  198. #if TORRENT_USE_ASSERTS
  199. size_t rpc_manager::allocation_size() const
  200. {
  201. return observer_size;
  202. }
  203. #endif
  204. #if TORRENT_USE_INVARIANT_CHECKS
  205. void rpc_manager::check_invariant() const
  206. {
  207. for (transactions_t::const_iterator i = m_transactions.begin()
  208. , end(m_transactions.end()); i != end; ++i)
  209. {
  210. TORRENT_ASSERT(*i);
  211. }
  212. }
  213. #endif
  214. void rpc_manager::unreachable(udp::endpoint const& ep)
  215. {
  216. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  217. TORRENT_LOG(rpc) << time_now_string() << " PORT_UNREACHABLE [ ip: " << ep << " ]";
  218. #endif
  219. for (transactions_t::iterator i = m_transactions.begin();
  220. i != m_transactions.end();)
  221. {
  222. TORRENT_ASSERT(*i);
  223. observer_ptr const& o = *i;
  224. if (o->target_ep() != ep) { ++i; continue; }
  225. observer_ptr ptr = *i;
  226. i = m_transactions.erase(i);
  227. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  228. TORRENT_LOG(rpc) << " found transaction [ tid: " << ptr->transaction_id() << " ]";
  229. #endif
  230. ptr->timeout();
  231. break;
  232. }
  233. }
  234. // defined in node.cpp
  235. void incoming_error(entry& e, char const* msg, int error_code = 203);
  236. bool rpc_manager::incoming(msg const& m, node_id* id, libtorrent::dht_settings const& settings)
  237. {
  238. INVARIANT_CHECK;
  239. if (m_destructing) return false;
  240. // we only deal with replies and errors, not queries
  241. TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r"
  242. || m.message.dict_find_string_value("y") == "e");
  243. // if we don't have the transaction id in our
  244. // request list, ignore the packet
  245. std::string transaction_id = m.message.dict_find_string_value("t");
  246. if (transaction_id.empty()) return false;
  247. std::string::const_iterator i = transaction_id.begin();
  248. int tid = transaction_id.size() != 2 ? -1 : io::read_uint16(i);
  249. observer_ptr o;
  250. for (transactions_t::iterator i = m_transactions.begin()
  251. , end(m_transactions.end()); i != end;)
  252. {
  253. TORRENT_ASSERT(*i);
  254. if ((*i)->transaction_id() != tid
  255. || m.addr.address() != (*i)->target_addr())
  256. {
  257. ++i;
  258. continue;
  259. }
  260. o = *i;
  261. i = m_transactions.erase(i);
  262. break;
  263. }
  264. if (!o)
  265. {
  266. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  267. TORRENT_LOG(rpc) << "Reply with unknown transaction id size: "
  268. << transaction_id.size() << " from " << m.addr;
  269. #endif
  270. // this isn't necessarily because the other end is doing
  271. // something wrong. This can also happen when we restart
  272. // the node, and we prematurely abort all outstanding
  273. // requests. Also, this opens up a potential magnification
  274. // attack.
  275. // entry e;
  276. // incoming_error(e, "invalid transaction id");
  277. // m_sock->send_packet(e, m.addr, 0);
  278. return false;
  279. }
  280. ptime now = time_now_hires();
  281. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  282. std::ofstream reply_stats("round_trip_ms.log", std::ios::app);
  283. reply_stats << m.addr << "\t" << total_milliseconds(now - o->sent())
  284. << std::endl;
  285. #endif
  286. lazy_entry const* ret_ent = m.message.dict_find_dict("r");
  287. if (ret_ent == 0)
  288. {
  289. // it may be an error
  290. ret_ent = m.message.dict_find_dict("e");
  291. o->timeout();
  292. if (ret_ent == NULL)
  293. {
  294. entry e;
  295. incoming_error(e, "missing 'r' key");
  296. m_sock->send_packet(e, m.addr, 0);
  297. }
  298. return false;
  299. }
  300. lazy_entry const* node_id_ent = ret_ent->dict_find_string("id");
  301. if (node_id_ent == 0 || node_id_ent->string_length() != 20)
  302. {
  303. o->timeout();
  304. entry e;
  305. incoming_error(e, "missing 'id' key");
  306. m_sock->send_packet(e, m.addr, 0);
  307. return false;
  308. }
  309. node_id nid = node_id(node_id_ent->string_ptr());
  310. if (settings.enforce_node_id && !verify_id(nid, m.addr.address()))
  311. {
  312. o->timeout();
  313. entry e;
  314. incoming_error(e, "invalid node ID");
  315. m_sock->send_packet(e, m.addr, 0);
  316. return false;
  317. }
  318. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  319. TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Reply with transaction id: "
  320. << tid << " from " << m.addr;
  321. #endif
  322. o->reply(m);
  323. *id = nid;
  324. int rtt = int(total_milliseconds(now - o->sent()));
  325. // we found an observer for this reply, hence the node is not spoofing
  326. // add it to the routing table
  327. return m_table.node_seen(*id, m.addr, rtt);
  328. }
  329. time_duration rpc_manager::tick()
  330. {
  331. INVARIANT_CHECK;
  332. const static int short_timeout = 1;
  333. const static int timeout = 15;
  334. // look for observers that have timed out
  335. if (m_transactions.empty()) return seconds(short_timeout);
  336. std::list<observer_ptr> timeouts;
  337. time_duration ret = seconds(short_timeout);
  338. ptime now = time_now();
  339. #if TORRENT_USE_ASSERTS
  340. ptime last = min_time();
  341. for (transactions_t::iterator i = m_transactions.begin();
  342. i != m_transactions.end(); ++i)
  343. {
  344. TORRENT_ASSERT((*i)->sent() >= last);
  345. last = (*i)->sent();
  346. }
  347. #endif
  348. for (transactions_t::iterator i = m_transactions.begin();
  349. i != m_transactions.end();)
  350. {
  351. observer_ptr o = *i;
  352. // if we reach an observer that hasn't timed out
  353. // break, because every observer after this one will
  354. // also not have timed out yet
  355. time_duration diff = now - o->sent();
  356. if (diff < seconds(timeout))
  357. {
  358. ret = seconds(timeout) - diff;
  359. break;
  360. }
  361. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  362. TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Timing out transaction id: "
  363. << (*i)->transaction_id() << " from " << o->target_ep();
  364. #endif
  365. i = m_transactions.erase(i);
  366. timeouts.push_back(o);
  367. }
  368. std::for_each(timeouts.begin(), timeouts.end(), boost::bind(&observer::timeout, _1));
  369. timeouts.clear();
  370. for (transactions_t::iterator i = m_transactions.begin();
  371. i != m_transactions.end(); ++i)
  372. {
  373. observer_ptr o = *i;
  374. // if we reach an observer that hasn't timed out
  375. // break, because every observer after this one will
  376. // also not have timed out yet
  377. time_duration diff = now - o->sent();
  378. if (diff < seconds(short_timeout))
  379. {
  380. ret = seconds(short_timeout) - diff;
  381. break;
  382. }
  383. // don't call short_timeout() again if we've
  384. // already called it once
  385. if (o->has_short_timeout()) continue;
  386. timeouts.push_back(o);
  387. }
  388. std::for_each(timeouts.begin(), timeouts.end(), boost::bind(&observer::short_timeout, _1));
  389. return ret;
  390. }
  391. void rpc_manager::add_our_id(entry& e)
  392. {
  393. e["id"] = m_our_id.to_string();
  394. }
  395. bool rpc_manager::invoke(entry& e, udp::endpoint target_addr
  396. , observer_ptr o)
  397. {
  398. INVARIANT_CHECK;
  399. if (m_destructing) return false;
  400. e["y"] = "q";
  401. entry& a = e["a"];
  402. add_our_id(a);
  403. std::string transaction_id;
  404. transaction_id.resize(2);
  405. char* out = &transaction_id[0];
  406. int tid = (random() ^ (random() << 5)) & 0xffff;
  407. io::write_uint16(tid, out);
  408. e["t"] = transaction_id;
  409. o->set_target(target_addr);
  410. o->set_transaction_id(tid);
  411. #ifdef TORRENT_DHT_VERBOSE_LOGGING
  412. TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] invoking "
  413. << e["q"].string() << " -> " << target_addr;
  414. #endif
  415. if (m_sock->send_packet(e, target_addr, 1))
  416. {
  417. m_transactions.push_back(o);
  418. #if TORRENT_USE_ASSERTS
  419. o->m_was_sent = true;
  420. #endif
  421. return true;
  422. }
  423. return false;
  424. }
  425. observer::~observer()
  426. {
  427. // if the message was sent, it must have been
  428. // reported back to the traversal_algorithm as
  429. // well. If it wasn't sent, it cannot have been
  430. // reported back
  431. TORRENT_ASSERT(m_was_sent == bool(flags & flag_done) || m_was_abandoned);
  432. TORRENT_ASSERT(!m_in_constructor);
  433. #if TORRENT_USE_ASSERTS
  434. TORRENT_ASSERT(m_in_use);
  435. m_in_use = false;
  436. #endif
  437. }
  438. } } // namespace libtorrent::dht