bool dos_blocker::incoming(address const& addr, time_point const now, dht_logger* logger) { TORRENT_UNUSED(logger); node_ban_entry* match = nullptr; node_ban_entry* min = m_ban_nodes; for (node_ban_entry* i = m_ban_nodes; i < m_ban_nodes + num_ban_nodes; ++i) { if (i->src == addr) { match = i; break; } if (i->count < min->count) min = i; else if (i->count == min->count && i->limit < min->limit) min = i; } if (match) { ++match->count; if (match->count >= m_message_rate_limit * 10) { if (now < match->limit) { if (match->count == m_message_rate_limit * 10) { #ifndef TORRENT_DISABLE_LOGGING if (logger != nullptr && logger->should_log(dht_logger::tracker)) { logger->log(dht_logger::tracker, "BANNING PEER [ ip: %s time: %d ms count: %d ]" , print_address(addr).c_str() , int(total_milliseconds((now - match->limit) + seconds(10))) , match->count); } #else TORRENT_UNUSED(logger); #endif // TORRENT_DISABLE_LOGGING // we've received too many messages in less than 10 seconds // from this node. Ignore it until it's silent for 5 minutes match->limit = now + seconds(m_block_timeout); } return false; } // the messages we received from this peer took more than 10 // seconds. Reset the counter and the timer match->count = 0; match->limit = now + seconds(10); } } else { min->count = 1; min->limit = now + seconds(10); min->src = addr; } return true; }
void condition_variable::wait_for(mutex::scoped_lock& l, time_duration rel_time) { TORRENT_ASSERT(l.locked()); ++m_num_waiters; l.unlock(); WaitForSingleObject(m_sem, total_milliseconds(rel_time)); l.lock(); --m_num_waiters; }
char const* time_now_string() { static const time_point start = clock_type::now(); static char ret[200]; int t = int(total_milliseconds(clock_type::now() - start)); int h = t / 1000 / 60 / 60; t -= h * 60 * 60 * 1000; int m = t / 1000 / 60; t -= m * 60 * 1000; int s = t / 1000; t -= s * 1000; int ms = t; std::snprintf(ret, sizeof(ret), "%02d:%02d:%02d.%03d", h, m, s, ms); return ret; }
TORRENT_EXTRA_EXPORT char const* time_now_string() { static const ptime start = time_now_hires(); static char ret[200]; int t = total_milliseconds(time_now_hires() - start); int h = t / 1000 / 60 / 60; t -= h * 60 * 60 * 1000; int m = t / 1000 / 60; t -= m * 60 * 1000; int s = t / 1000; t -= s * 1000; int ms = t; snprintf(ret, sizeof(ret), "%02d:%02d:%02d.%03d", h, m, s, ms); return ret; }
void ConnectionImpl <WebSocket04>::setPingTimer () { if (pingFreq_ <= 0) return; if (auto con = m_connection.lock ()) { auto t = boost::posix_time::seconds (pingFreq_); auto ms = t.total_milliseconds(); con->set_timer ( ms, std::bind ( beast::weak_fn (&ConnectionImpl <WebSocket04>::pingTimer, shared_from_this()), beast::asio::placeholders::error)); } }
void ConnectionImpl <WebSocket04>::setPingTimer () { auto freq = getConfig ().WEBSOCKET_PING_FREQ; if (freq <= 0) return; if (auto con = m_connection.lock ()) { auto t = boost::posix_time::seconds (freq); auto ms = t.total_milliseconds(); con->set_timer ( ms, std::bind ( beast::weak_fn (&ConnectionImpl <WebSocket04>::pingTimer, shared_from_this()), beast::asio::placeholders::error)); } }
int unchoke_sort(std::vector<peer_connection*>& peers , int max_upload_rate , time_duration unchoke_interval , aux::session_settings const& sett) { int upload_slots = sett.get_int(settings_pack::unchoke_slots_limit); // ==== BitTyrant ==== // // if we're using the bittyrant unchoker, go through all peers that // we have unchoked already, and adjust our estimated reciprocation // rate. If the peer has reciprocated, lower the estimate, if it hasn't, // increase the estimate (this attempts to optimize "ROI" of upload // capacity, by sending just enough to be reciprocated). // For more information, see: http://bittyrant.cs.washington.edu/ if (sett.get_int(settings_pack::choking_algorithm) == settings_pack::bittyrant_choker) { for (std::vector<peer_connection*>::const_iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection* p = *i; if (p->is_choked() || !p->is_interesting()) continue; if (!p->has_peer_choked()) { // we're unchoked, we may want to lower our estimated // reciprocation rate p->decrease_est_reciprocation_rate(); } else { // we've unchoked this peer, and it hasn't reciprocated // we may want to increase our estimated reciprocation rate p->increase_est_reciprocation_rate(); } } // if we're using the bittyrant choker, sort peers by their return // on investment. i.e. download rate / upload rate std::sort(peers.begin(), peers.end() , boost::bind(&bittyrant_unchoke_compare, _1, _2)); int upload_capacity_left = max_upload_rate; // now, figure out how many peers should be unchoked. We deduct the // estimated reciprocation rate from our upload_capacity estimate // until there none left upload_slots = 0; for (std::vector<peer_connection*>::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection* p = *i; TORRENT_ASSERT(p); if (p->est_reciprocation_rate() > upload_capacity_left) break; ++upload_slots; upload_capacity_left -= p->est_reciprocation_rate(); } return upload_slots; } // ==== rate-based ==== // // The rate based unchoker looks at our upload rate to peers, and find // a balance between number of upload slots and the rate we achieve. The // intention is to not spread upload bandwidth too thin, but also to not // unchoke few enough peers to not be able to saturate the up-link. // this is done by traversing the peers sorted by our upload rate to // them in decreasing rates. For each peer we increase our threshold // by 1 kB/s. The first peer we get to to whom we upload slower than // the threshold, we stop and that's the number of unchoke slots we have. if (sett.get_int(settings_pack::choking_algorithm) == settings_pack::rate_based_choker) { // first reset the number of unchoke slots, because we'll calculate // it purely based on the current state of our peers. upload_slots = 0; // TODO: optimize this using partial_sort or something. We don't need // to sort the entire list // TODO: make the comparison function a free function and move it // into this cpp file std::sort(peers.begin(), peers.end() , boost::bind(&upload_rate_compare, _1, _2)); // TODO: make configurable int rate_threshold = 1024; for (std::vector<peer_connection*>::const_iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection const& p = **i; int rate = int(p.uploaded_in_last_round() * 1000 / total_milliseconds(unchoke_interval)); if (rate < rate_threshold) break; ++upload_slots; // TODO: make configurable rate_threshold += 1024; } ++upload_slots; } // sorts the peers that are eligible for unchoke by download rate and // secondary by total upload. The reason for this is, if all torrents are // being seeded, the download rate will be 0, and the peers we have sent // the least to should be unchoked // we use partial sort here, because we only care about the top // upload_slots peers. if (sett.get_int(settings_pack::seed_choking_algorithm) == settings_pack::round_robin) { int pieces = sett.get_int(settings_pack::seeding_piece_quota); std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_rr, _1, _2, pieces)); } else if (sett.get_int(settings_pack::seed_choking_algorithm) == settings_pack::fastest_upload) { std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_fastest_upload, _1, _2)); } else if (sett.get_int(settings_pack::seed_choking_algorithm) == settings_pack::anti_leech) { std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_anti_leech, _1, _2)); } else { TORRENT_ASSERT(false && "unknown seed choking algorithm"); int pieces = sett.get_int(settings_pack::seeding_piece_quota); std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_rr, _1, _2, pieces)); } return upload_slots; }
bool rpc_manager::incoming(msg const& m, node_id* id) { INVARIANT_CHECK; if (m_destructing) return false; // we only deal with replies and errors, not queries TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r" || m.message.dict_find_string_value("y") == "e"); // if we don't have the transaction id in our // request list, ignore the packet std::string transaction_id = m.message.dict_find_string_value("t"); if (transaction_id.empty()) return false; std::string::const_iterator ptr = transaction_id.begin(); int tid = transaction_id.size() != 2 ? -1 : io::read_uint16(ptr); observer_ptr o; std::pair<transactions_t::iterator, transactions_t::iterator> range = m_transactions.equal_range(tid); for (transactions_t::iterator i = range.first; i != range.second; ++i) { if (m.addr.address() != i->second->target_addr()) continue; o = i->second; i = m_transactions.erase(i); break; } if (!o) { #ifndef TORRENT_DISABLE_LOGGING if (m_table.native_endpoint(m.addr)) { m_log->log(dht_logger::rpc_manager, "reply with unknown transaction id size: %d from %s" , int(transaction_id.size()), print_endpoint(m.addr).c_str()); } #endif // this isn't necessarily because the other end is doing // something wrong. This can also happen when we restart // the node, and we prematurely abort all outstanding // requests. Also, this opens up a potential magnification // attack. // entry e; // incoming_error(e, "invalid transaction id"); // m_sock->send_packet(e, m.addr); return false; } time_point now = clock_type::now(); #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "round trip time(ms): %" PRId64 " from %s" , total_milliseconds(now - o->sent()), print_endpoint(m.addr).c_str()); #endif if (m.message.dict_find_string_value("y") == "e") { // It's an error. #ifndef TORRENT_DISABLE_LOGGING bdecode_node err = m.message.dict_find_list("e"); if (err && err.list_size() >= 2 && err.list_at(0).type() == bdecode_node::int_t && err.list_at(1).type() == bdecode_node::string_t) { m_log->log(dht_logger::rpc_manager, "reply with error from %s: (%" PRId64 ") %s" , print_endpoint(m.addr).c_str() , err.list_int_value_at(0) , err.list_string_value_at(1).c_str()); } else { m_log->log(dht_logger::rpc_manager, "reply with (malformed) error from %s" , print_endpoint(m.addr).c_str()); } #endif // Logically, we should call o->reply(m) since we get a reply. // a reply could be "response" or "error", here the reply is an "error". // if the reply is an "error", basically the observer could/will // do nothing with it, especially when observer::reply() is intended to // handle a "response", not an "error". // A "response" should somehow call algorithm->finished(), and an error/timeout // should call algorithm->failed(). From this point of view, // we should call o->timeout() instead of o->reply(m) because o->reply() // will call algorithm->finished(). o->timeout(); return false; } bdecode_node ret_ent = m.message.dict_find_dict("r"); if (!ret_ent) { o->timeout(); return false; } bdecode_node node_id_ent = ret_ent.dict_find_string("id"); if (!node_id_ent || node_id_ent.string_length() != 20) { o->timeout(); return false; } node_id nid = node_id(node_id_ent.string_ptr()); if (m_settings.enforce_node_id && !verify_id(nid, m.addr.address())) { o->timeout(); return false; } #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "[%p] reply with transaction id: %d from %s" , static_cast<void*>(o->algorithm()), int(transaction_id.size()) , print_endpoint(m.addr).c_str()); #endif o->reply(m); *id = nid; int rtt = int(total_milliseconds(now - o->sent())); // we found an observer for this reply, hence the node is not spoofing // add it to the routing table return m_table.node_seen(*id, m.addr, rtt); }
void STORCommand::Execute() { namespace pt = boost::posix_time; namespace gd = boost::gregorian; fs::VirtualPath path(fs::PathFromUser(argStr)); util::Error e(acl::path::Filter(client.User(), path.Basename())); if (!e) { control.Reply(ftp::ActionNotOkay, "File name contains one or more invalid characters."); throw cmd::NoPostScriptError(); } off_t offset = data.RestartOffset(); if (offset > 0 && data.DataType() == ftp::DataType::ASCII) { control.Reply(ftp::BadCommandSequence, "Resume not supported on ASCII data type."); throw cmd::NoPostScriptError(); } if (!exec::PreCheck(client, path)) throw cmd::NoPostScriptError(); switch(ftp::Counter::Upload().Start(client.User().ID(), client.User().MaxSimUp(), client.User().HasFlag(acl::Flag::Exempt))) { case ftp::CounterResult::PersonalFail : { std::ostringstream os; os << "You have reached your maximum of " << client.User().MaxSimUp() << " simultaneous uploads(s)."; control.Reply(ftp::ActionNotOkay, os.str()); throw cmd::NoPostScriptError(); } case ftp::CounterResult::GlobalFail : { control.Reply(ftp::ActionNotOkay, "The server has reached it's maximum number of simultaneous uploads."); throw cmd::NoPostScriptError(); } case ftp::CounterResult::Okay : break; } auto countGuard = util::MakeScopeExit([&]{ ftp::Counter::Upload().Stop(client.User().ID()); }); if (data.DataType() == ftp::DataType::ASCII && !cfg::Get().AsciiUploads().Allowed(path.ToString())) { control.Reply(ftp::ActionNotOkay, "File can't be uploaded in ASCII, change to BINARY."); throw cmd::NoPostScriptError(); } fs::FileSinkPtr fout; try { if (data.RestartOffset() > 0) fout = fs::AppendFile(client.User(), path, data.RestartOffset()); else fout = fs::CreateFile(client.User(), path); } catch (const util::SystemError& e) { if (data.RestartOffset() == 0 && e.Errno() == EEXIST) { DupeMessage(path); } else { std::ostringstream os; os << "Unable to " << (data.RestartOffset() > 0 ? "append" : "create") << " file: " << e.Message(); control.Reply(ftp::ActionNotOkay, os.str()); } throw cmd::NoPostScriptError(); } catch (const util::RuntimeError& e) { std::ostringstream os; os << "Unable to " << (data.RestartOffset() > 0 ? "append" : "create") << " file: " << e.Message(); control.Reply(ftp::ActionNotOkay, os.str()); throw cmd::NoPostScriptError(); } bool fileOkay = data.RestartOffset() > 0; auto fileGuard = util::MakeScopeExit([&] { if (!fileOkay) { try { fs::DeleteFile(fs::MakeReal(path)); } catch (std::exception& e) { logs::Error("Failed to delete failed upload: %1%", e.what()); } } }); std::stringstream os; os << "Opening " << (data.DataType() == ftp::DataType::ASCII ? "ASCII" : "BINARY") << " connection for upload of " << fs::MakePretty(path).ToString(); if (data.Protection()) os << " using TLS/SSL"; os << "."; control.Reply(ftp::TransferStatusOkay, os.str()); try { data.Open(ftp::TransferType::Upload); } catch (const util::net::NetworkError&e ) { if (!data.RestartOffset()) fs::DeleteFile(fs::MakeReal(path)); control.Reply(ftp::CantOpenDataConnection, "Unable to open data connection: " + e.Message()); throw cmd::NoPostScriptError(); } auto dataGuard = util::MakeScopeExit([&] { if (data.State().Type() != ftp::TransferType::None) { data.Close(); if (data.State().Bytes() > 0) { db::stats::Upload(client.User(), data.State().Bytes() / 1024, data.State().Duration().total_milliseconds()); } } }); if (!data.ProtectionOkay()) { std::ostringstream os; os << "TLS is enforced on " << (data.IsFXP() ? "FXP" : "data") << " transfers."; control.Reply(ftp::ProtocolNotSupported, os.str()); return; } auto section = cfg::Get().SectionMatch(path.ToString()); auto transferLogGuard = util::MakeScopeExit([&] { if (cfg::Get().TransferLog().Uploads()) { bool okay = !std::uncaught_exception(); logs::Transfer(fs::MakeReal(path).ToString(), "up", client.User().Name(), client.User().PrimaryGroup(), (data.State().StartTime() - pt::ptime(gd::date(1970, 1, 1))).total_microseconds() / 1000000.0, data.State().Bytes() / 1024, data.State().Duration().total_microseconds() / 1000000.0, okay, section ? section->Name() : std::string()); } }); const size_t bufferSize = cfg::Get().DataBufferSize(); bool calcCrc = CalcCRC(path); std::unique_ptr<util::CRC32> crc32(cfg::Get().AsyncCRC() ? new util::AsyncCRC32(bufferSize, 10) : new util::CRC32()); bool aborted = false; fileOkay = false; try { ftp::UploadSpeedControl speedControl(client, path); ftp::OnlineTransferUpdater onlineUpdater(boost::this_thread::get_id(), stats::Direction::Upload, data.State().StartTime()); std::vector<char> asciiBuffer; std::vector<char> buffer; buffer.resize(bufferSize); while (true) { size_t len = data.Read(&buffer[0], buffer.size()); const char *bufp = buffer.data(); if (data.DataType() == ftp::DataType::ASCII) { ftp::ASCIITranscodeSTOR(bufp, len, asciiBuffer); len = asciiBuffer.size(); bufp = asciiBuffer.data(); } data.State().Update(len); fout->write(bufp, len); if (calcCrc) crc32->Update(reinterpret_cast<const uint8_t*>(bufp), len); onlineUpdater.Update(data.State().Bytes()); speedControl.Apply(); } } catch (const util::net::EndOfStream&) { } catch (const ftp::TransferAborted&) { aborted = true; } catch (const util::net::NetworkError& e) { control.Reply(ftp::DataCloseAborted, "Error while reading from data connection: " + e.Message()); throw cmd::NoPostScriptError(); } catch (const std::ios_base::failure& e) { control.Reply(ftp::DataCloseAborted, "Error while writing to disk: " + std::string(e.what())); throw cmd::NoPostScriptError(); } catch (const ftp::ControlError& e) { e.Rethrow(); } catch (const ftp::MinimumSpeedError& e) { logs::Debug("Aborted slow upload by %1%. %2% lower than %3%", client.User().Name(), stats::AutoUnitSpeedString(e.Speed()), stats::AutoUnitSpeedString(e.Limit())); aborted = true; } fout->close(); data.Close(); e = fs::Chmod(fs::MakeReal(path), completeMode); if (!e) control.PartReply(ftp::DataClosedOkay, "Failed to chmod upload: " + e.Message()); auto duration = data.State().Duration(); double speed = stats::CalculateSpeed(data.State().Bytes(), duration); if (aborted) { control.Reply(ftp::DataClosedOkay, "Transfer aborted @ " + stats::AutoUnitSpeedString(speed / 1024)); throw cmd::NoPostScriptError(); } if (exec::PostCheck(client, path, calcCrc ? crc32->HexString() : "000000", speed, section ? section->Name() : "")) { fileOkay = true; bool nostats = !section || acl::path::FileAllowed<acl::path::Nostats>(client.User(), path); db::stats::Upload(client.User(), data.State().Bytes() / 1024, duration.total_milliseconds(), nostats ? "" : section->Name()); client.User().IncrSectionCredits(section && section->SeparateCredits() ? section->Name() : "", data.State().Bytes() / 1024 * stats::UploadRatio(client.User(), path, section)); } control.Reply(ftp::DataClosedOkay, "Transfer finished @ " + stats::AutoUnitSpeedString(speed / 1024)); (void) countGuard; (void) fileGuard; (void) dataGuard; }
bool rpc_manager::incoming(msg const& m, node_id* id) { INVARIANT_CHECK; if (m_destructing) return false; // we only deal with replies, not queries TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r"); // if we don't have the transaction id in our // request list, ignore the packet std::string transaction_id = m.message.dict_find_string_value("t"); std::string::const_iterator i = transaction_id.begin(); int tid = transaction_id.size() != 2 ? -1 : io::read_uint16(i); observer_ptr o; for (transactions_t::iterator i = m_transactions.begin() , end(m_transactions.end()); i != end; ++i) { TORRENT_ASSERT(*i); if ((*i)->transaction_id() != tid) continue; if (m.addr.address() != (*i)->target_addr()) continue; o = *i; m_transactions.erase(i); break; } if (!o) { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(rpc) << "Reply with invalid transaction id size: " << transaction_id.size() << " from " << m.addr; #endif entry e; incoming_error(e, "invalid transaction id"); m_send(m_userdata, e, m.addr, 0); return false; } #ifdef TORRENT_DHT_VERBOSE_LOGGING std::ofstream reply_stats("round_trip_ms.log", std::ios::app); reply_stats << m.addr << "\t" << total_milliseconds(time_now_hires() - o->sent()) << std::endl; #endif lazy_entry const* ret_ent = m.message.dict_find_dict("r"); if (ret_ent == 0) { entry e; incoming_error(e, "missing 'r' key"); m_send(m_userdata, e, m.addr, 0); return false; } lazy_entry const* node_id_ent = ret_ent->dict_find_string("id"); if (node_id_ent == 0 || node_id_ent->string_length() != 20) { entry e; incoming_error(e, "missing 'id' key"); m_send(m_userdata, e, m.addr, 0); return false; } lazy_entry const* ext_ip = ret_ent->dict_find_string("ip"); if (ext_ip && ext_ip->string_length() == 4) { // this node claims we use the wrong node-ID! address_v4::bytes_type b; memcpy(&b[0], ext_ip->string_ptr(), 4); m_ext_ip(address_v4(b), aux::session_impl::source_dht, m.addr.address()); } #if TORRENT_USE_IPV6 else if (ext_ip && ext_ip->string_length() == 16) { // this node claims we use the wrong node-ID! address_v6::bytes_type b; memcpy(&b[0], ext_ip->string_ptr(), 16); m_ext_ip(address_v6(b), aux::session_impl::source_dht, m.addr.address()); } #endif #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Reply with transaction id: " << tid << " from " << m.addr; #endif o->reply(m); *id = node_id(node_id_ent->string_ptr()); // we found an observer for this reply, hence the node is not spoofing // add it to the routing table return m_table.node_seen(*id, m.addr); }
double TimeDuration::operator /(const TimeDuration& b) const { return double(total_milliseconds()) / double(b.total_milliseconds()); }
TimeDuration TimeDuration::operator *(const double& b) const { return ptime::milliseconds(double(total_milliseconds()) * b); }
void bandwidth_manager::update_quotas(time_duration const& dt) { if (m_abort) return; if (m_queue.empty()) return; INVARIANT_CHECK; boost::int64_t dt_milliseconds = total_milliseconds(dt); if (dt_milliseconds > 3000) dt_milliseconds = 3000; // for each bandwidth channel, call update_quota(dt) std::vector<bandwidth_channel*> channels; queue_t tm; for (queue_t::iterator i = m_queue.begin(); i != m_queue.end();) { if (i->peer->is_disconnecting()) { m_queued_bytes -= i->request_size - i->assigned; // return all assigned quota to all the // bandwidth channels this peer belongs to for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) { bandwidth_channel* bwc = i->channel[j]; bwc->return_quota(i->assigned); } i->assigned = 0; tm.push_back(*i); i = m_queue.erase(i); continue; } for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) { bandwidth_channel* bwc = i->channel[j]; bwc->tmp = 0; } ++i; } for (queue_t::iterator i = m_queue.begin() , end(m_queue.end()); i != end; ++i) { for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) { bandwidth_channel* bwc = i->channel[j]; if (bwc->tmp == 0) channels.push_back(bwc); TORRENT_ASSERT(INT_MAX - bwc->tmp > i->priority); bwc->tmp += i->priority; } } for (std::vector<bandwidth_channel*>::iterator i = channels.begin() , end(channels.end()); i != end; ++i) { (*i)->update_quota(int(dt_milliseconds)); } for (queue_t::iterator i = m_queue.begin(); i != m_queue.end();) { int a = i->assign_bandwidth(); if (i->assigned == i->request_size || (i->ttl <= 0 && i->assigned > 0)) { a += i->request_size - i->assigned; TORRENT_ASSERT(i->assigned <= i->request_size); tm.push_back(*i); i = m_queue.erase(i); } else { ++i; } m_queued_bytes -= a; } while (!tm.empty()) { bw_request& bwr = tm.back(); bwr.peer->assign_bandwidth(m_channel, bwr.assigned); tm.pop_back(); } }
bool rpc_manager::incoming(msg const& m, node_id* id , libtorrent::dht_settings const& settings) { INVARIANT_CHECK; if (m_destructing) return false; // we only deal with replies and errors, not queries TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r" || m.message.dict_find_string_value("y") == "e"); // if we don't have the transaction id in our // request list, ignore the packet std::string transaction_id = m.message.dict_find_string_value("t"); if (transaction_id.empty()) return false; std::string::const_iterator i = transaction_id.begin(); int tid = transaction_id.size() != 2 ? -1 : io::read_uint16(i); observer_ptr o; std::pair<transactions_t::iterator, transactions_t::iterator> range = m_transactions.equal_range(tid); for (transactions_t::iterator i = range.first; i != range.second; ++i) { if (m.addr.address() != i->second->target_addr()) continue; o = i->second; i = m_transactions.erase(i); break; } if (!o) { #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "reply with unknown transaction id size: %d from %s" , int(transaction_id.size()), print_endpoint(m.addr).c_str()); #endif // this isn't necessarily because the other end is doing // something wrong. This can also happen when we restart // the node, and we prematurely abort all outstanding // requests. Also, this opens up a potential magnification // attack. // entry e; // incoming_error(e, "invalid transaction id"); // m_sock->send_packet(e, m.addr, 0); return false; } time_point now = clock_type::now(); #ifndef TORRENT_DISABLE_LOGGING std::ofstream reply_stats("round_trip_ms.log", std::ios::app); reply_stats << m.addr << "\t" << total_milliseconds(now - o->sent()) << std::endl; #endif bdecode_node ret_ent = m.message.dict_find_dict("r"); if (!ret_ent) { // it may be an error ret_ent = m.message.dict_find("e"); o->timeout(); if (!ret_ent) { entry e; incoming_error(e, "missing 'r' key"); m_sock->send_packet(e, m.addr, 0); } return false; } bdecode_node node_id_ent = ret_ent.dict_find_string("id"); if (!node_id_ent || node_id_ent.string_length() != 20) { o->timeout(); entry e; incoming_error(e, "missing 'id' key"); m_sock->send_packet(e, m.addr, 0); return false; } node_id nid = node_id(node_id_ent.string_ptr()); if (settings.enforce_node_id && !verify_id(nid, m.addr.address())) { o->timeout(); entry e; incoming_error(e, "invalid node ID"); m_sock->send_packet(e, m.addr, 0); return false; } #ifndef TORRENT_DISABLE_LOGGING m_log->log(dht_logger::rpc_manager, "[%p] reply with transaction id: %d from %s" , o->algorithm(), int(transaction_id.size()) , print_endpoint(m.addr).c_str()); #endif o->reply(m); *id = nid; int rtt = int(total_milliseconds(now - o->sent())); // we found an observer for this reply, hence the node is not spoofing // add it to the routing table return m_table.node_seen(*id, m.addr, rtt); }