From 6df0de565fb08380827abc59a78d7feb03348e61 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 19 Jan 2024 20:08:55 +0100 Subject: [PATCH] Check media requests on the server more carefully --- src/clientiface.h | 16 ++++++++---- src/network/serverpackethandler.cpp | 12 ++++----- src/server.cpp | 40 ++++++++++++++++++++--------- src/server.h | 2 +- 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/clientiface.h b/src/clientiface.h index 138e7fc79..1df67b568 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -284,10 +284,10 @@ public: return m_blocks_sent.find(p) != m_blocks_sent.end(); } - // Increments timeouts and removes timed-out blocks from list - // NOTE: This doesn't fix the server-not-sending-block bug - // because it is related to emerging, not sending. - //void RunSendingTimeouts(float dtime, float timeout); + bool markMediaSent(const std::string &name) { + auto insert_result = m_media_sent.emplace(name); + return insert_result.second; // true = was inserted + } void PrintInfo(std::ostream &o) { @@ -310,7 +310,7 @@ public: ClientState getState() const { return m_state; } - std::string getName() const { return m_name; } + const std::string &getName() const { return m_name; } void setName(const std::string &name) { m_name = name; } @@ -394,6 +394,12 @@ private: const s16 m_max_gen_distance; const bool m_occ_cull; + /* + Set of media files the client has already requested + We won't send the same file twice to avoid bandwidth consumption attacks. + */ + std::unordered_set m_media_sent; + /* Blocks that are currently on the line. This is used for throttling the sending of blocks. diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 934afb055..253dcdba8 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -356,24 +356,24 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) void Server::handleCommand_RequestMedia(NetworkPacket* pkt) { - std::vector tosend; + std::unordered_set tosend; u16 numfiles; *pkt >> numfiles; session_t peer_id = pkt->getPeerId(); - infostream << "Sending " << numfiles << " files to " << - getPlayerName(peer_id) << std::endl; - verbosestream << "TOSERVER_REQUEST_MEDIA: requested file(s)" << std::endl; + verbosestream << "Client " << getPlayerName(peer_id) + << " requested media file(s):\n"; for (u16 i = 0; i < numfiles; i++) { std::string name; *pkt >> name; - tosend.emplace_back(name); - verbosestream << " " << name << std::endl; + tosend.emplace(name); + verbosestream << " " << name << "\n"; } + verbosestream << std::flush; sendRequestedMedia(peer_id, tosend); } diff --git a/src/server.cpp b/src/server.cpp index c30753246..f71d8d31d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -137,6 +137,7 @@ void *ServerThread::run() } catch (con::PeerNotFoundException &e) { infostream<<"Server: PeerNotFoundException"<setAsyncFatalError(e.what()); } catch (LuaError &e) { @@ -2671,29 +2672,44 @@ struct SendableMedia }; void Server::sendRequestedMedia(session_t peer_id, - const std::vector &tosend) + const std::unordered_set &tosend) { - verbosestream<<"Server::sendRequestedMedia(): " - <<"Sending files to client"<getName() << std::endl; /* Read files */ // Put 5kB in one bunch (this is not accurate) - u32 bytes_per_bunch = 5000; + const u32 bytes_per_bunch = 5000; - std::vector< std::vector > file_bunches; + std::vector> file_bunches; file_bunches.emplace_back(); u32 file_size_bunch_total = 0; for (const std::string &name : tosend) { - if (m_media.find(name) == m_media.end()) { + auto it = m_media.find(name); + + if (it == m_media.end()) { errorstream<<"Server::sendRequestedMedia(): Client asked for " <<"unknown file \""<<(name)<<"\""<second; - const auto &m = m_media[name]; + // no_announce <=> usually ephemeral dynamic media, which may + // have duplicate filenames. So we can't check it. + if (!m.no_announce) { + if (!client->markMediaSent(name)) { + infostream << "Server::sendRequestedMedia(): Client asked has " + "requested \"" << name << "\" before, not sending it again." + << std::endl; + continue; + } + } // Read data std::string data; @@ -2717,11 +2733,11 @@ void Server::sendRequestedMedia(session_t peer_id, /* Create and send packets */ - u16 num_bunches = file_bunches.size(); + const u16 num_bunches = file_bunches.size(); for (u16 i = 0; i < num_bunches; i++) { + auto &bunch = file_bunches[i]; /* - u16 command - u16 total number of texture bunches + u16 total number of media bunches u16 index of this bunch u32 number of files in this bunch for each file { @@ -2735,14 +2751,14 @@ void Server::sendRequestedMedia(session_t peer_id, NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id); pkt << num_bunches << i << (u32) file_bunches[i].size(); - for (const SendableMedia &j : file_bunches[i]) { + for (auto &j : bunch) { pkt << j.name; pkt.putLongString(j.data); } verbosestream << "Server::sendRequestedMedia(): bunch " << i << "/" << num_bunches - << " files=" << file_bunches[i].size() + << " files=" << bunch.size() << " size=" << pkt.getSize() << std::endl; Send(&pkt); } diff --git a/src/server.h b/src/server.h index bec2fe1cf..2915c88cd 100644 --- a/src/server.h +++ b/src/server.h @@ -515,7 +515,7 @@ private: void fillMediaCache(); void sendMediaAnnouncement(session_t peer_id, const std::string &lang_code); void sendRequestedMedia(session_t peer_id, - const std::vector &tosend); + const std::unordered_set &tosend); void stepPendingDynMediaCallbacks(float dtime); // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all)