void on_read(lt::error_code const& ec, std::size_t bytes_transferred) { if (ec) return; using libtorrent::entry; using libtorrent::bdecode; int pos; error_code err; // since the simulation is single threaded, we can get away with just // allocating a single of these static bdecode_node msg; int ret = bdecode(m_buffer, m_buffer + bytes_transferred, msg, err, &pos, 10, 500); if (ret != 0) return; if (msg.type() != bdecode_node::dict_t) return; libtorrent::dht::msg m(msg, m_ep); dht().incoming(m); sock().async_receive_from(asio::mutable_buffers_1(m_buffer, sizeof(m_buffer)) , m_ep, [&](lt::error_code const& ec, std::size_t bytes_transferred) { this->on_read(ec, bytes_transferred); }); }
// translate bittorrent kademlia message into the generice kademlia message // used by the library void dht_tracker::on_receive(error_code const& error, size_t bytes_transferred) try { if (error == asio::error::operation_aborted) return; if (!m_socket.is_open()) return; int current_buffer = m_buffer; m_buffer = (m_buffer + 1) & 1; m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0] , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer] , m_strand.wrap(bind(&dht_tracker::on_receive, self(), _1, _2))); if (error) return; node_ban_entry* match = 0; node_ban_entry* min = m_ban_nodes; ptime now = time_now(); for (node_ban_entry* i = m_ban_nodes; i < m_ban_nodes + num_ban_nodes; ++i) { if (i->src == m_remote_endpoint[current_buffer]) { match = i; break; } if (i->count < min->count) min = i; } if (match) { ++match->count; if (match->count >= 20) { if (now < match->limit) { #ifdef TORRENT_DHT_VERBOSE_LOGGING if (match->count == 20) { TORRENT_LOG(dht_tracker) << time_now_string() << " BANNING PEER [ ip: " << m_remote_endpoint[current_buffer] << " | " "time: " << total_seconds((now - match->limit) + seconds(5)) << " | count: " << match->count << " ]"; } #endif // we've received 20 messages in less than 5 seconds from // this node. Ignore it until it's silent for 5 minutes match->limit = now + minutes(5); return; } // we got 50 messages from this peer, but it was in // more than 5 seconds. Reset the counter and the timer match->count = 0; match->limit = now + seconds(5); } } else { min->count = 1; min->limit = now + seconds(5); min->src = m_remote_endpoint[current_buffer]; } #ifdef TORRENT_DHT_VERBOSE_LOGGING ++m_total_message_input; m_total_in_bytes += bytes_transferred; #endif try { using libtorrent::entry; using libtorrent::bdecode; TORRENT_ASSERT(bytes_transferred > 0); entry e = bdecode(m_in_buf[current_buffer].begin() , m_in_buf[current_buffer].end()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << time_now_string() << " RECEIVED [" << m_remote_endpoint[current_buffer] << "]:"; #endif libtorrent::dht::msg m; m.message_id = 0; m.addr = m_remote_endpoint[current_buffer]; m.transaction_id = e["t"].string(); #ifdef TORRENT_DHT_VERBOSE_LOGGING try { entry const* ver = e.find_key("v"); if (!ver) throw std::exception(); std::string const& client = ver->string(); if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT")) { ++m_ut_message_input; TORRENT_LOG(dht_tracker) << " client: uTorrent"; } else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT")) { ++m_lt_message_input; TORRENT_LOG(dht_tracker) << " client: libtorrent"; } else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP")) { ++m_mp_message_input; TORRENT_LOG(dht_tracker) << " client: MooPolice"; } else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR")) { ++m_gr_message_input; TORRENT_LOG(dht_tracker) << " client: GetRight"; } else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MO")) { ++m_mo_message_input; TORRENT_LOG(dht_tracker) << " client: Mono Torrent"; } else { TORRENT_LOG(dht_tracker) << " client: " << client; } } catch (std::exception&) { TORRENT_LOG(dht_tracker) << " client: generic"; }; #endif std::string const& msg_type = e["y"].string(); if (msg_type == "r") { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " reply: transaction: " << m.transaction_id; #endif m.reply = true; entry const& r = e["r"]; std::string const& id = r["id"].string(); if (id.size() != 20) throw std::runtime_error("invalid size of id"); std::copy(id.begin(), id.end(), m.id.begin()); if (entry const* n = r.find_key("values")) { m.peers.clear(); if (n->list().size() == 1) { // assume it's mainline format std::string const& peers = n->list().front().string(); std::string::const_iterator i = peers.begin(); std::string::const_iterator end = peers.end(); while (std::distance(i, end) >= 6) m.peers.push_back(read_v4_endpoint<tcp::endpoint>(i)); } else { // assume it's uTorrent/libtorrent format read_endpoint_list<tcp::endpoint>(n, m.peers); } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); #endif } m.nodes.clear(); if (entry const* n = r.find_key("nodes")) { std::string const& nodes = n->string(); std::string::const_iterator i = nodes.begin(); std::string::const_iterator end = nodes.end(); while (std::distance(i, end) >= 26) { node_id id; std::copy(i, i + 20, id.begin()); i += 20; m.nodes.push_back(libtorrent::dht::node_entry( id, read_v4_endpoint<udp::endpoint>(i))); } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); #endif } if (entry const* n = r.find_key("nodes2")) { entry::list_type const& contacts = n->list(); for (entry::list_type::const_iterator i = contacts.begin() , end(contacts.end()); i != end; ++i) { std::string const& p = i->string(); if (p.size() < 6 + 20) continue; std::string::const_iterator in = p.begin(); node_id id; std::copy(in, in + 20, id.begin()); in += 20; if (p.size() == 6 + 20) m.nodes.push_back(libtorrent::dht::node_entry( id, read_v4_endpoint<udp::endpoint>(in))); else if (p.size() == 18 + 20) m.nodes.push_back(libtorrent::dht::node_entry( id, read_v6_endpoint<udp::endpoint>(in))); } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size(); #endif } entry const* token = r.find_key("token"); if (token) m.write_token = *token; } else if (msg_type == "q") { m.reply = false; entry const& a = e["a"]; std::string const& id = a["id"].string(); if (id.size() != 20) throw std::runtime_error("invalid size of id"); std::copy(id.begin(), id.end(), m.id.begin()); std::string request_kind(e["q"].string()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " query: " << request_kind; #endif if (request_kind == "ping") { m.message_id = libtorrent::dht::messages::ping; } else if (request_kind == "find_node") { std::string const& target = a["target"].string(); if (target.size() != 20) throw std::runtime_error("invalid size of target id"); std::copy(target.begin(), target.end(), m.info_hash.begin()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " target: " << boost::lexical_cast<std::string>(m.info_hash); #endif m.message_id = libtorrent::dht::messages::find_node; } else if (request_kind == "get_peers") { std::string const& info_hash = a["info_hash"].string(); if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash"); std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); m.message_id = libtorrent::dht::messages::get_peers; #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " info_hash: " << boost::lexical_cast<std::string>(m.info_hash); #endif } else if (request_kind == "announce_peer") { #ifdef TORRENT_DHT_VERBOSE_LOGGING ++m_announces; #endif std::string const& info_hash = a["info_hash"].string(); if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash"); std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); m.port = a["port"].integer(); m.write_token = a["token"]; m.message_id = libtorrent::dht::messages::announce_peer; #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " info_hash: " << boost::lexical_cast<std::string>(m.info_hash); TORRENT_LOG(dht_tracker) << " port: " << m.port; if (!m_dht.verify_token(m)) ++m_failed_announces; #endif } else { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : " << request_kind; #endif throw std::runtime_error("unsupported request: " + request_kind); } } else if (msg_type == "e") { entry::list_type const& list = e["e"].list(); m.message_id = messages::error; m.error_msg = list.back().string(); m.error_code = list.front().integer(); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " incoming error: " << m.error_code << " " << m.error_msg; #endif throw std::runtime_error("DHT error message"); } else { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : " << msg_type; #endif throw std::runtime_error("unsupported message type: " + msg_type); } #ifdef TORRENT_DHT_VERBOSE_LOGGING if (!m.reply) { ++m_queries_received[m.message_id]; m_queries_bytes_received[m.message_id] += int(bytes_transferred); } TORRENT_LOG(dht_tracker) << e; #endif TORRENT_ASSERT(m.message_id != messages::error); m_dht.incoming(m); } catch (std::exception& e) { #ifdef TORRENT_DHT_VERBOSE_LOGGING int current_buffer = (m_buffer + 1) & 1; std::string msg(m_in_buf[current_buffer].begin() , m_in_buf[current_buffer].begin() + bytes_transferred); TORRENT_LOG(dht_tracker) << "invalid incoming packet: " << e.what() << "\n" << msg << "\n"; #endif } } catch (std::exception& e) { TORRENT_ASSERT(false); };