diff -r 7c1e1a42a057 dcpp/AdcCommand.h --- a/dcpp/AdcCommand.h Thu Sep 07 18:08:34 2017 -0700 +++ b/dcpp/AdcCommand.h Wed Oct 11 18:25:42 2017 +0300 @@ -182,12 +182,17 @@ template class CommandHandler { -public: - void dispatch(const string& aLine, bool nmdc = false) { +public: + inline void dispatch(const string& aLine) noexcept { + dispatch(aLine, false); + } + + template + void dispatch(const string& aLine, bool nmdc, ArgT&&... args) noexcept { try { AdcCommand c(aLine, nmdc); - -#define C(n) case AdcCommand::CMD_##n: ((T*)this)->handle(AdcCommand::n(), c); break; + +#define C(n) case AdcCommand::CMD_##n: ((T*)this)->handle(AdcCommand::n(), c, std::forward(args)...); break; switch(c.getCommand()) { C(SUP); C(STA); diff -r 7c1e1a42a057 dcpp/SearchManager.cpp --- a/dcpp/SearchManager.cpp Thu Sep 07 18:08:34 2017 -0700 +++ b/dcpp/SearchManager.cpp Wed Oct 11 18:25:42 2017 +0300 @@ -171,140 +171,162 @@ return 0; } -void SearchManager::onData(const string& x, const string& remoteIp) { - if(x.compare(0, 4, "$SR ") == 0) { - string::size_type i, j; - // Directories: $SR <0x20><0x20>/<0x05><0x20>() - // Files: $SR <0x20><0x05><0x20>/<0x05><0x20>() - i = 4; - if( (j = x.find(' ', i)) == string::npos) { +void SearchManager::onData(const string& x, const string& remoteIp) { + if (x.compare(0, 1, "$") == 0) { + // NMDC commands + if (x.compare(1, 3, "SR ") == 0) { + onSR(x, remoteIp); + } else { + dcdebug("Unknown NMDC command received via UDP: %s\n", x.c_str()); + } + + return; + } + + // ADC commands + + // ADC commands must end with \n + if (x[x.length() - 1] != 0x0a) { + dcdebug("Invalid UDP data received: %s (no newline)\n", x.c_str()); + return; + } + + if (!Text::validateUtf8(x)) { + dcdebug("UTF-8 valition failed for received UDP data: %s\n", x.c_str()); + return; + } + + // Dispatch without newline + dispatch(x.substr(0, x.length() - 1), false, remoteIp); +} + +void SearchManager::handle(AdcCommand::RES, AdcCommand& c, const string& remoteIp) noexcept { + if (c.getParameters().empty()) + return; + + string cid = c.getParam(0); + if (cid.size() != 39) + return; + + UserPtr user = ClientManager::getInstance()->findUser(CID(cid)); + if (!user) + return; + + // This should be handled by AdcCommand really... + c.getParameters().erase(c.getParameters().begin()); + + onRES(c, user, remoteIp); +} + +void SearchManager::onSR(const string& x, const string& remoteIp) { + string::size_type i, j; + // Directories: $SR <0x20><0x20>/<0x05><0x20>() + // Files: $SR <0x20><0x05><0x20>/<0x05><0x20>() + i = 4; + if ((j = x.find(' ', i)) == string::npos) { + return; + } + string nick = x.substr(i, j - i); + i = j + 1; + + // A file has 2 0x05, a directory only one + size_t cnt = count(x.begin() + j, x.end(), 0x05); + + SearchResult::Types type = SearchResult::TYPE_FILE; + string file; + int64_t size = 0; + + if (cnt == 1) { + // We have a directory...find the first space beyond the first 0x05 from the back + // (dirs might contain spaces as well...clever protocol, eh?) + type = SearchResult::TYPE_DIRECTORY; + // Get past the hubname that might contain spaces + if ((j = x.rfind(0x05)) == string::npos) { return; } - string nick = x.substr(i, j-i); - i = j + 1; - - // A file has 2 0x05, a directory only one - size_t cnt = count(x.begin() + j, x.end(), 0x05); - - SearchResult::Types type = SearchResult::TYPE_FILE; - string file; - int64_t size = 0; - - if(cnt == 1) { - // We have a directory...find the first space beyond the first 0x05 from the back - // (dirs might contain spaces as well...clever protocol, eh?) - type = SearchResult::TYPE_DIRECTORY; - // Get past the hubname that might contain spaces - if((j = x.rfind(0x05)) == string::npos) { - return; - } - // Find the end of the directory info - if((j = x.rfind(' ', j-1)) == string::npos) { - return; - } - if(j < i + 1) { - return; - } - file = x.substr(i, j-i) + '\\'; - } else if(cnt == 2) { - if( (j = x.find((char)5, i)) == string::npos) { - return; - } - file = x.substr(i, j-i); - i = j + 1; - if( (j = x.find(' ', i)) == string::npos) { - return; - } - size = Util::toInt64(x.substr(i, j-i)); - } - i = j + 1; - - if( (j = x.find('/', i)) == string::npos) { + // Find the end of the directory info + if ((j = x.rfind(' ', j - 1)) == string::npos) { return; } - int freeSlots = Util::toInt(x.substr(i, j-i)); - i = j + 1; - if( (j = x.find((char)5, i)) == string::npos) { + if (j < i + 1) { return; } - int slots = Util::toInt(x.substr(i, j-i)); - i = j + 1; - if( (j = x.rfind(" (")) == string::npos) { + file = x.substr(i, j - i) + '\\'; + } else if (cnt == 2) { + if ((j = x.find((char)5, i)) == string::npos) { return; } - string hubName = x.substr(i, j-i); - i = j + 2; - if( (j = x.rfind(')')) == string::npos) { + file = x.substr(i, j - i); + i = j + 1; + if ((j = x.find(' ', i)) == string::npos) { return; } + size = Util::toInt64(x.substr(i, j - i)); + } + i = j + 1; - HintedUser user; + if ((j = x.find('/', i)) == string::npos) { + return; + } + int freeSlots = Util::toInt(x.substr(i, j - i)); + i = j + 1; + if ((j = x.find((char)5, i)) == string::npos) { + return; + } + int slots = Util::toInt(x.substr(i, j - i)); + i = j + 1; + if ((j = x.rfind(" (")) == string::npos) { + return; + } + string hubName = x.substr(i, j - i); + i = j + 2; + if ((j = x.rfind(')')) == string::npos) { + return; + } - user.hint = ClientManager::getInstance()->findHub(x.substr(i, j - i)); - if(user.hint.empty()) { - // Could happen if hub has multiple URLs / IPs - user = ClientManager::getInstance()->findLegacyUser(nick); - if(!user) - return; + HintedUser user; + + user.hint = ClientManager::getInstance()->findHub(x.substr(i, j - i)); + if (user.hint.empty()) { + // Could happen if hub has multiple URLs / IPs + user = ClientManager::getInstance()->findLegacyUser(nick); + if (!user) + return; + } + + string encoding = ClientManager::getInstance()->findHubEncoding(user.hint); + nick = Text::toUtf8(nick, encoding); + file = Text::toUtf8(file, encoding); + hubName = Text::toUtf8(hubName, encoding); + + if (!user) { + user.user = ClientManager::getInstance()->findUser(nick, user.hint); + if (!user) + return; + } + + Style style; + { + auto lock = ClientManager::getInstance()->lock(); + auto ou = ClientManager::getInstance()->findOnlineUser(user); + if (ou) { + style = ou->getIdentity().getStyle(); } + } - string encoding = ClientManager::getInstance()->findHubEncoding(user.hint); - nick = Text::toUtf8(nick, encoding); - file = Text::toUtf8(file, encoding); - hubName = Text::toUtf8(hubName, encoding); + string tth; + if (hubName.compare(0, 4, "TTH:") == 0) { + tth = hubName.substr(4); + StringList names = ClientManager::getInstance()->getHubNames(user); + hubName = names.empty() ? _("Offline") : Util::toString(names); + } - if(!user) { - user.user = ClientManager::getInstance()->findUser(nick, user.hint); - if(!user) - return; - } + if (tth.empty() && type == SearchResult::TYPE_FILE) { + return; + } - Style style; - { - auto lock = ClientManager::getInstance()->lock(); - auto ou = ClientManager::getInstance()->findOnlineUser(user); - if (ou) { - style = ou->getIdentity().getStyle(); - } - } - - string tth; - if(hubName.compare(0, 4, "TTH:") == 0) { - tth = hubName.substr(4); - StringList names = ClientManager::getInstance()->getHubNames(user); - hubName = names.empty() ? _("Offline") : Util::toString(names); - } - - if(tth.empty() && type == SearchResult::TYPE_FILE) { - return; - } - - fire(SearchManagerListener::SR(), SearchResultPtr(new SearchResult(user, type, slots, - freeSlots, size, file, hubName, remoteIp, TTHValue(tth), Util::emptyString, style))); - - } else if(x.compare(1, 4, "RES ") == 0 && x[x.length() - 1] == 0x0a) { - AdcCommand c(x.substr(0, x.length()-1)); - if(c.getParameters().empty()) - return; - string cid = c.getParam(0); - if(cid.size() != 39) - return; - - UserPtr user = ClientManager::getInstance()->findUser(CID(cid)); - if(!user) - return; - - // This should be handled by AdcCommand really... - c.getParameters().erase(c.getParameters().begin()); - - onRES(c, user, remoteIp); - - } /*else if(x.compare(1, 4, "SCH ") == 0 && x[x.length() - 1] == 0x0a) { - try { - respond(AdcCommand(x.substr(0, x.length()-1))); - } catch(ParseException& ) { - } - }*/ // Needs further DoS investigation + fire(SearchManagerListener::SR(), SearchResultPtr(new SearchResult(user, type, slots, + freeSlots, size, file, hubName, remoteIp, TTHValue(tth), Util::emptyString, style))); } void SearchManager::onRES(const AdcCommand& cmd, const UserPtr& from, const string& remoteIp) { diff -r 7c1e1a42a057 dcpp/SearchManager.h --- a/dcpp/SearchManager.h Thu Sep 07 18:08:34 2017 -0700 +++ b/dcpp/SearchManager.h Wed Oct 11 18:25:42 2017 +0300 @@ -21,6 +21,7 @@ #include "SettingsManager.h" +#include "AdcCommand.h" #include "Socket.h" #include "Thread.h" #include "Singleton.h" @@ -33,7 +34,7 @@ class SearchManager; class SocketException; -class SearchManager : public Speaker, public Singleton, public Thread +class SearchManager : public Speaker, public Singleton, public Thread, private CommandHandler { public: enum SizeModes { @@ -78,6 +79,7 @@ void onData(const string& data, const string& remoteIp = Util::emptyString); void onRES(const AdcCommand& cmd, const UserPtr& from, const string& removeIp = Util::emptyString); + void onSR(const string& x, const string& remoteIP = Util::emptyString); int32_t timeToSearch() { return 5 - (static_cast(GET_TICK() - lastSearch) / 1000); @@ -87,7 +89,13 @@ return timeToSearch() <= 0; } -private: +private: + friend class CommandHandler; + + void handle(AdcCommand::RES, AdcCommand& c, const string& remoteIp) noexcept; + + // Ignore any other ADC commands for now + template void handle(T, AdcCommand&, const string&) { } std::unique_ptr socket; string port;