// prevent request means that the total number of requests has // overflown. This query failed because it was the oldest one. // So, if this is true, don't make another request void traversal_algorithm::failed(observer_ptr o, int flags) { TORRENT_ASSERT(m_invoke_count >= 0); if (m_results.empty()) return; TORRENT_ASSERT(o->flags & observer::flag_queried); if (flags & short_timeout) { // short timeout means that it has been more than // two seconds since we sent the request, and that // we'll most likely not get a response. But, in case // we do get a late response, keep the handler // around for some more, but open up the slot // by increasing the branch factor if ((o->flags & observer::flag_short_timeout) == 0) ++m_branch_factor; o->flags |= observer::flag_short_timeout; #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(traversal) << "[" << this << "] 1ST_TIMEOUT " << " id: " << o->id() << " distance: " << distance_exp(m_target, o->id()) << " addr: " << o->target_ep() << " branch-factor: " << m_branch_factor << " invoke-count: " << m_invoke_count; #endif } else { o->flags |= observer::flag_failed; // if this flag is set, it means we increased the // branch factor for it, and we should restore it if (o->flags & observer::flag_short_timeout) --m_branch_factor; #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(traversal) << "[" << this << "] TIMEOUT " << " id: " << o->id() << " distance: " << distance_exp(m_target, o->id()) << " addr: " << o->target_ep() << " branch-factor: " << m_branch_factor << " invoke-count: " << m_invoke_count; #endif // don't tell the routing table about // node ids that we just generated ourself if ((o->flags & observer::flag_no_id) == 0) m_node.m_table.node_failed(o->id(), o->target_ep()); ++m_timeouts; --m_invoke_count; TORRENT_ASSERT(m_invoke_count >= 0); } if (flags & prevent_request) { --m_branch_factor; if (m_branch_factor <= 0) m_branch_factor = 1; } bool is_done = add_requests(); if (is_done) done(); }
void traversal_algorithm::done() { #ifdef TORRENT_DHT_VERBOSE_LOGGING int results_target = m_num_target_nodes; int closest_target = 160; for (std::vector<observer_ptr>::iterator i = m_results.begin() , end(m_results.end()); i != end && results_target > 0; ++i) { boost::intrusive_ptr<observer> o = *i; if (o->flags & observer::flag_alive) { TORRENT_ASSERT(o->flags & observer::flag_queried); TORRENT_LOG(traversal) << "[" << this << "] " << results_target << " id: " << o->id() << " distance: " << distance_exp(m_target, o->id()) << " address: " << o->target_ep() ; --results_target; int dist = distance_exp(m_target, o->id()); if (dist < closest_target) closest_target = dist; } } TORRENT_LOG(traversal) << "[" << this << "] COMPLETED " << "distance: " << closest_target << " type: " << name() ; #endif // delete all our references to the observer objects so // they will in turn release the traversal algorithm m_results.clear(); }
void traversal_algorithm::done() { #ifndef TORRENT_DISABLE_LOGGING int results_target = m_node.m_table.bucket_size(); int closest_target = 160; #endif for (auto const& o : m_results) { if ((o->flags & (observer::flag_queried | observer::flag_failed)) == observer::flag_queried) { // set the done flag on any outstanding queries to prevent them from // calling finished() or failed() after we've already declared the traversal // done o->flags |= observer::flag_done; } #ifndef TORRENT_DISABLE_LOGGING dht_observer* logger = get_node().observer(); if (results_target > 0 && (o->flags & observer::flag_alive) && logger != nullptr && logger->should_log(dht_logger::traversal)) { TORRENT_ASSERT(o->flags & observer::flag_queried); char hex_id[41]; aux::to_hex(o->id(), hex_id); logger->log(dht_logger::traversal , "[%p] id: %s distance: %d addr: %s" , static_cast<void*>(this), hex_id, closest_target , print_endpoint(o->target_ep()).c_str()); --results_target; int dist = distance_exp(m_target, o->id()); if (dist < closest_target) closest_target = dist; } #endif } #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer() != nullptr) { get_node().observer()->log(dht_logger::traversal , "[%p] COMPLETED distance: %d type: %s" , static_cast<void*>(this), closest_target, name()); } #endif // delete all our references to the observer objects so // they will in turn release the traversal algorithm m_results.clear(); m_invoke_count = 0; }
bool traversal_algorithm::add_requests() { int results_target = m_num_target_nodes; // this only counts outstanding requests at the top of the // target list. This is <= m_invoke count. m_invoke_count // is the total number of outstanding requests, including // old ones that may be waiting on nodes much farther behind // the current point we've reached in the search. int outstanding = 0; // if we're doing aggressive lookups, we keep branch-factor // outstanding requests _at the tops_ of the result list. Otherwise // we just keep any branch-factor outstanding requests bool agg = m_node.settings().aggressive_lookups; // Find the first node that hasn't already been queried. // and make sure that the 'm_branch_factor' top nodes // stay queried at all times (obviously ignoring failed nodes) // and without surpassing the 'result_target' nodes (i.e. k=8) // this is a slight variation of the original paper which instead // limits the number of outstanding requests, this limits the // number of good outstanding requests. It will use more traffic, // but is intended to speed up lookups for (std::vector<observer_ptr>::iterator i = m_results.begin() , end(m_results.end()); i != end && results_target > 0 && (agg ? outstanding < m_branch_factor : m_invoke_count < m_branch_factor); ++i) { observer* o = i->get(); if (o->flags & observer::flag_alive) { TORRENT_ASSERT(o->flags & observer::flag_queried); --results_target; continue; } if (o->flags & observer::flag_queried) { // if it's queried, not alive and not failed, it // must be currently in flight if ((o->flags & observer::flag_failed) == 0) ++outstanding; continue; } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(traversal) << "[" << this << "] INVOKE " << " nodes-left: " << (m_results.end() - i) << " top-invoke-count: " << outstanding << " invoke-count: " << m_invoke_count << " branch-factor: " << m_branch_factor << " distance: " << distance_exp(m_target, (*i)->id()) << " type: " << name() ; #endif o->flags |= observer::flag_queried; if (invoke(*i)) { TORRENT_ASSERT(m_invoke_count >= 0); ++m_invoke_count; ++outstanding; } else { o->flags |= observer::flag_failed; } } // this is the completion condition. If we found m_num_target_nodes // (i.e. k=8) completed results, without finding any still // outstanding requests, we're done. // also, if invoke count is 0, it means we didn't even find 'k' // working nodes, we still have to terminate though. return (results_target == 0 && outstanding == 0) || m_invoke_count == 0; }
void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags) { TORRENT_ASSERT(m_node.m_rpc.allocation_size() >= sizeof(find_data_observer)); void* ptr = m_node.m_rpc.allocate_observer(); if (ptr == 0) { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(traversal) << "[" << this << "] failed to allocate memory for observer. aborting!"; #endif done(); return; } observer_ptr o = new_observer(ptr, addr, id); if (id.is_all_zeros()) { o->set_id(generate_random_id()); o->flags |= observer::flag_no_id; } o->flags |= flags; TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() , boost::bind( compare_ref , boost::bind(&observer::id, _1) , boost::bind(&observer::id, _2) , m_target) )); std::vector<observer_ptr>::iterator i = std::lower_bound( m_results.begin() , m_results.end() , o , boost::bind( compare_ref , boost::bind(&observer::id, _1) , boost::bind(&observer::id, _2) , m_target ) ); if (i == m_results.end() || (*i)->id() != id) { if (m_node.settings().restrict_search_ips && !(flags & observer::flag_initial)) { // don't allow multiple entries from IPs very close to each other std::vector<observer_ptr>::iterator j = std::find_if( m_results.begin(), m_results.end(), boost::bind(&compare_ip_cidr, _1, o)); if (j != m_results.end()) { // we already have a node in this search with an IP very // close to this one. We know that it's not the same, because // it claims a different node-ID. Ignore this to avoid attacks #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(traversal) << "[" << this << "] IGNORING result " << "id: " << o->id() << " address: " << o->target_addr() << " existing node: " << (*j)->id() << " " << (*j)->target_addr() << " distance: " << distance_exp(m_target, o->id()) << " type: " << name() ; #endif return; } } TORRENT_ASSERT((o->flags & observer::flag_no_id) || std::find_if(m_results.begin(), m_results.end() , boost::bind(&observer::id, _1) == id) == m_results.end()); #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(traversal) << "[" << this << "] ADD id: " << id << " address: " << addr << " distance: " << distance_exp(m_target, id) << " invoke-count: " << m_invoke_count << " type: " << name() ; #endif i = m_results.insert(i, o); TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() , boost::bind( compare_ref , boost::bind(&observer::id, _1) , boost::bind(&observer::id, _2) , m_target) )); } if (m_results.size() > 100) { #if TORRENT_USE_ASSERTS for (int i = 100; i < int(m_results.size()); ++i) m_results[i]->m_was_abandoned = true; #endif m_results.resize(100); } }
bool traversal_algorithm::add_requests() { int results_target = m_node.m_table.bucket_size(); // this only counts outstanding requests at the top of the // target list. This is <= m_invoke count. m_invoke_count // is the total number of outstanding requests, including // old ones that may be waiting on nodes much farther behind // the current point we've reached in the search. int outstanding = 0; // if we're doing aggressive lookups, we keep branch-factor // outstanding requests _at the tops_ of the result list. Otherwise // we just keep any branch-factor outstanding requests bool agg = m_node.settings().aggressive_lookups; // Find the first node that hasn't already been queried. // and make sure that the 'm_branch_factor' top nodes // stay queried at all times (obviously ignoring failed nodes) // and without surpassing the 'result_target' nodes (i.e. k=8) // this is a slight variation of the original paper which instead // limits the number of outstanding requests, this limits the // number of good outstanding requests. It will use more traffic, // but is intended to speed up lookups for (std::vector<observer_ptr>::iterator i = m_results.begin() , end(m_results.end()); i != end && results_target > 0 && (agg ? outstanding < m_branch_factor : m_invoke_count < m_branch_factor); ++i) { observer* o = i->get(); if (o->flags & observer::flag_alive) { TORRENT_ASSERT(o->flags & observer::flag_queried); --results_target; continue; } if (o->flags & observer::flag_queried) { // if it's queried, not alive and not failed, it // must be currently in flight if ((o->flags & observer::flag_failed) == 0) ++outstanding; continue; } #ifndef TORRENT_DISABLE_LOGGING dht_observer* logger = get_node().observer(); if (logger != nullptr && logger->should_log(dht_logger::traversal)) { char hex_id[41]; aux::to_hex(o->id(), hex_id); logger->log(dht_logger::traversal , "[%p] INVOKE nodes-left: %d top-invoke-count: %d " "invoke-count: %d branch-factor: %d " "distance: %d id: %s addr: %s type: %s" , static_cast<void*>(this), int(m_results.end() - i), outstanding, int(m_invoke_count) , int(m_branch_factor), distance_exp(m_target, o->id()), hex_id , print_address(o->target_addr()).c_str(), name()); } #endif o->flags |= observer::flag_queried; if (invoke(*i)) { TORRENT_ASSERT(m_invoke_count < (std::numeric_limits<std::int16_t>::max)()); ++m_invoke_count; ++outstanding; } else { o->flags |= observer::flag_failed; } } // this is the completion condition. If we found m_node.m_table.bucket_size() // (i.e. k=8) completed results, without finding any still // outstanding requests, we're done. // also, if invoke count is 0, it means we didn't even find 'k' // working nodes, we still have to terminate though. return (results_target == 0 && outstanding == 0) || m_invoke_count == 0; }
// prevent request means that the total number of requests has // overflown. This query failed because it was the oldest one. // So, if this is true, don't make another request void traversal_algorithm::failed(observer_ptr o, int const flags) { // don't tell the routing table about // node ids that we just generated ourself if ((o->flags & observer::flag_no_id) == 0) m_node.m_table.node_failed(o->id(), o->target_ep()); if (m_results.empty()) return; bool decrement_branch_factor = false; TORRENT_ASSERT(o->flags & observer::flag_queried); if (flags & short_timeout) { // short timeout means that it has been more than // two seconds since we sent the request, and that // we'll most likely not get a response. But, in case // we do get a late response, keep the handler // around for some more, but open up the slot // by increasing the branch factor if ((o->flags & observer::flag_short_timeout) == 0) { TORRENT_ASSERT(m_branch_factor < (std::numeric_limits<std::int16_t>::max)()); ++m_branch_factor; } o->flags |= observer::flag_short_timeout; #ifndef TORRENT_DISABLE_LOGGING dht_observer* logger = get_node().observer(); if (logger != nullptr && logger->should_log(dht_logger::traversal)) { char hex_id[41]; aux::to_hex(o->id(), hex_id); logger->log(dht_logger::traversal , "[%p] 1ST_TIMEOUT id: %s distance: %d addr: %s branch-factor: %d " "invoke-count: %d type: %s" , static_cast<void*>(this), hex_id, distance_exp(m_target, o->id()) , print_address(o->target_addr()).c_str(), m_branch_factor , m_invoke_count, name()); } #endif } else { o->flags |= observer::flag_failed; // if this flag is set, it means we increased the // branch factor for it, and we should restore it decrement_branch_factor = (o->flags & observer::flag_short_timeout) != 0; #ifndef TORRENT_DISABLE_LOGGING dht_observer* logger = get_node().observer(); if (logger != nullptr && logger->should_log(dht_logger::traversal)) { char hex_id[41]; aux::to_hex(o->id(), hex_id); logger->log(dht_logger::traversal , "[%p] TIMEOUT id: %s distance: %d addr: %s branch-factor: %d " "invoke-count: %d type: %s" , static_cast<void*>(this), hex_id, distance_exp(m_target, o->id()) , print_address(o->target_addr()).c_str(), m_branch_factor , m_invoke_count, name()); } #endif ++m_timeouts; TORRENT_ASSERT(m_invoke_count > 0); --m_invoke_count; } // this is another reason to decrement the branch factor, to prevent another // request from filling this slot. Only ever decrement once per response though decrement_branch_factor |= (flags & prevent_request); if (decrement_branch_factor) { TORRENT_ASSERT(m_branch_factor > 0); --m_branch_factor; if (m_branch_factor <= 0) m_branch_factor = 1; } bool const is_done = add_requests(); if (is_done) done(); }
void traversal_algorithm::add_entry(node_id const& id , udp::endpoint const& addr, unsigned char const flags) { TORRENT_ASSERT(m_node.m_rpc.allocation_size() >= sizeof(find_data_observer)); auto o = new_observer(addr, id); if (!o) { #ifndef TORRENT_DISABLE_LOGGING if (get_node().observer() != nullptr) { get_node().observer()->log(dht_logger::traversal, "[%p] failed to allocate memory or observer. aborting!" , static_cast<void*>(this)); } #endif done(); return; } if (id.is_all_zeros()) { o->set_id(generate_random_id()); o->flags |= observer::flag_no_id; } o->flags |= flags; TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() , [this](observer_ptr const& lhs, observer_ptr const& rhs) { return compare_ref(lhs->id(), rhs->id(), m_target); })); auto iter = std::lower_bound(m_results.begin(), m_results.end(), o , [this](observer_ptr const& lhs, observer_ptr const& rhs) { return compare_ref(lhs->id(), rhs->id(), m_target); }); if (iter == m_results.end() || (*iter)->id() != id) { if (m_node.settings().restrict_search_ips && !(flags & observer::flag_initial)) { #if TORRENT_USE_IPV6 if (o->target_addr().is_v6()) { address_v6::bytes_type addr_bytes = o->target_addr().to_v6().to_bytes(); address_v6::bytes_type::const_iterator prefix_it = addr_bytes.begin(); std::uint64_t const prefix6 = detail::read_uint64(prefix_it); if (m_peer6_prefixes.insert(prefix6).second) goto add_result; } else #endif { // mask the lower octet std::uint32_t const prefix4 = o->target_addr().to_v4().to_ulong() & 0xffffff00; if (m_peer4_prefixes.insert(prefix4).second) goto add_result; } // we already have a node in this search with an IP very // close to this one. We know that it's not the same, because // it claims a different node-ID. Ignore this to avoid attacks #ifndef TORRENT_DISABLE_LOGGING dht_observer* logger = get_node().observer(); if (logger != nullptr && logger->should_log(dht_logger::traversal)) { char hex_id[41]; aux::to_hex(o->id(), hex_id); logger->log(dht_logger::traversal , "[%p] traversal DUPLICATE node. id: %s addr: %s type: %s" , static_cast<void*>(this), hex_id, print_address(o->target_addr()).c_str(), name()); } #endif return; } add_result: TORRENT_ASSERT((o->flags & observer::flag_no_id) || std::none_of(m_results.begin(), m_results.end() , [&id](observer_ptr const& ob) { return ob->id() == id; })); #ifndef TORRENT_DISABLE_LOGGING dht_observer* logger = get_node().observer(); if (logger != nullptr && logger->should_log(dht_logger::traversal)) { char hex_id[41]; aux::to_hex(id, hex_id); logger->log(dht_logger::traversal , "[%p] ADD id: %s addr: %s distance: %d invoke-count: %d type: %s" , static_cast<void*>(this), hex_id, print_endpoint(addr).c_str() , distance_exp(m_target, id), m_invoke_count, name()); } #endif iter = m_results.insert(iter, o); TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() , [this](observer_ptr const& lhs, observer_ptr const& rhs) { return compare_ref(lhs->id(), rhs->id(), m_target); })); } if (m_results.size() > 100) { for (int i = 100; i < int(m_results.size()); ++i) { if ((m_results[i]->flags & (observer::flag_queried | observer::flag_failed | observer::flag_alive)) == observer::flag_queried) { // set the done flag on any outstanding queries to prevent them from // calling finished() or failed() m_results[i]->flags |= observer::flag_done; TORRENT_ASSERT(m_invoke_count > 0); --m_invoke_count; } #if TORRENT_USE_ASSERTS m_results[i]->m_was_abandoned = true; #endif } m_results.resize(100); } }