ClientManager.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006-2010 Jacek Sieka, arnetheduck on gmail point com
00003  *
00004  * This program is free software; you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published by
00006  * the Free Software Foundation; either version 2 of the License, or
00007  * (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 #include "adchpp.h"
00020 
00021 #include "ClientManager.h"
00022 
00023 #include "File.h"
00024 #include "Client.h"
00025 #include "LogManager.h"
00026 #include "TimerManager.h"
00027 #include "SocketManager.h"
00028 #include "TigerHash.h"
00029 #include "Encoder.h"
00030 #include "version.h"
00031 
00032 namespace adchpp {
00033 
00034 using namespace std;
00035 
00036 ClientManager* ClientManager::instance = 0;
00037 const string ClientManager::className = "ClientManager";
00038 
00039 ClientManager::ClientManager() throw() : loginTimeout(30 * 1000) {
00040     hub.addSupports(AdcCommand::toFourCC("BASE"));
00041     hub.addSupports(AdcCommand::toFourCC("TIGR"));
00042 
00043     SocketManager::getInstance()->setIncomingHandler(std::bind(&ClientManager::handleIncoming, this, std::placeholders::_1));
00044 }
00045 
00046 ClientManager::~ClientManager() throw() {
00047 
00048 }
00049 
00050 Bot* ClientManager::createBot(const Bot::SendHandler& handler) {
00051     Bot* ret = new Bot(makeSID(), handler);
00052     return ret;
00053 }
00054 
00055 void ClientManager::regBot(Bot& bot) {
00056     enterIdentify(bot, false);
00057     enterNormal(bot, false, true);
00058     cids.insert(make_pair(bot.getCID(), &bot));
00059     nicks.insert(make_pair(bot.getField("NI"), &bot));
00060 }
00061 
00062 void ClientManager::send(const AdcCommand& cmd) throw() {
00063     if(cmd.getPriority() == AdcCommand::PRIORITY_IGNORE) {
00064         return;
00065     }
00066 
00067     bool all = false;
00068     switch(cmd.getType()) {
00069     case AdcCommand::TYPE_BROADCAST:
00070         all = true; // Fallthrough
00071     case AdcCommand::TYPE_FEATURE: {
00072         for(EntityIter i = entities.begin(); i != entities.end(); ++i) {
00073             if(all || !i->second->isFiltered(cmd.getFeatures())) {
00074                 maybeSend(*i->second, cmd);
00075             }
00076         }
00077     }
00078         break;
00079     case AdcCommand::TYPE_DIRECT: // Fallthrough
00080     case AdcCommand::TYPE_ECHO: {
00081         Entity* e = getEntity(cmd.getTo());
00082         if(e) {
00083             maybeSend(*e, cmd);
00084 
00085             if(cmd.getType() == AdcCommand::TYPE_ECHO) {
00086                 e = getEntity(cmd.getFrom());
00087                 if(e) {
00088                     maybeSend(*e, cmd);
00089                 }
00090             }
00091         }
00092     }
00093         break;
00094     }
00095 }
00096 
00097 void ClientManager::maybeSend(Entity& c, const AdcCommand& cmd) {
00098     bool ok = true;
00099     signalSend_(c, cmd, ok);
00100     if(ok) {
00101         c.send(cmd);
00102     }
00103 }
00104 
00105 void ClientManager::sendToAll(const BufferPtr& buf) throw() {
00106     for(EntityIter i = entities.begin(); i != entities.end(); ++i) {
00107         i->second->send(buf);
00108     }
00109 }
00110 
00111 size_t ClientManager::getQueuedBytes() throw() {
00112     size_t total = 0;
00113 
00114     for(EntityIter i = entities.begin(); i != entities.end(); ++i) {
00115         total += i->second->getQueuedBytes();
00116     }
00117 
00118     return total;
00119 }
00120 
00121 void ClientManager::sendTo(const BufferPtr& buffer, uint32_t to) {
00122     EntityIter i = entities.find(to);
00123     if(i != entities.end()) {
00124         i->second->send(buffer);
00125     }
00126 }
00127 
00128 void ClientManager::handleIncoming(const ManagedSocketPtr& socket) throw() {
00129     Client::create(socket, makeSID());
00130 }
00131 
00132 uint32_t ClientManager::makeSID() {
00133     while(true) {
00134         union {
00135             uint32_t sid;
00136             char chars[4];
00137         } sid;
00138         sid.chars[0] = Encoder::base32Alphabet[Util::rand(sizeof(Encoder::base32Alphabet))];
00139         sid.chars[1] = Encoder::base32Alphabet[Util::rand(sizeof(Encoder::base32Alphabet))];
00140         sid.chars[2] = Encoder::base32Alphabet[Util::rand(sizeof(Encoder::base32Alphabet))];
00141         sid.chars[3] = Encoder::base32Alphabet[Util::rand(sizeof(Encoder::base32Alphabet))];
00142         if(sid.sid != 0 && entities.find(sid.sid) == entities.end()) {
00143             return sid.sid;
00144         }
00145     }
00146 }
00147 
00148 void ClientManager::onConnected(Client& c) throw() {
00149     dcdebug("%s connected\n", AdcCommand::fromSID(c.getSID()).c_str());
00150     // First let's check if any clients have passed the login timeout...
00151     uint32_t timeout = GET_TICK() - getLoginTimeout();
00152     while(!logins.empty() && (timeout > logins.front().second)) {
00153         Client* cc = logins.front().first;
00154 
00155         dcdebug("ClientManager: Login timeout in state %d\n", cc->getState());
00156         cc->disconnect(Util::REASON_LOGIN_TIMEOUT);
00157         logins.pop_front();
00158     }
00159 
00160     logins.push_back(make_pair(&c, GET_TICK()));
00161 
00162     signalConnected_(c);
00163 }
00164 
00165 void ClientManager::onReceive(Entity& c, AdcCommand& cmd) throw() {
00166     if(!(cmd.getType() == AdcCommand::TYPE_BROADCAST || cmd.getType() == AdcCommand::TYPE_DIRECT || cmd.getType()
00167         == AdcCommand::TYPE_ECHO || cmd.getType() == AdcCommand::TYPE_FEATURE || cmd.getType() == AdcCommand::TYPE_HUB)) {
00168         c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC, "Invalid command type"));
00169         c.disconnect(Util::REASON_INVALID_COMMAND_TYPE);
00170         return;
00171     }
00172 
00173     bool ok = true;
00174     signalReceive_(c, cmd, ok);
00175 
00176     if(ok) {
00177         if(!dispatch(c, cmd)) {
00178             return;
00179         }
00180     }
00181 
00182     send(cmd);
00183 }
00184 
00185 void ClientManager::onBadLine(Client& c, const string& aLine) throw() {
00186     signalBadLine_(c, aLine);
00187 }
00188 
00189 void ClientManager::badState(Entity& c, const AdcCommand& cmd) throw() {
00190     c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_BAD_STATE, "Invalid state for command").addParam("FC",
00191         cmd.getFourCC()));
00192     c.disconnect(Util::REASON_BAD_STATE);
00193 }
00194 
00195 bool ClientManager::handleDefault(Entity& c, AdcCommand& cmd) throw() {
00196     if(c.getState() != Entity::STATE_NORMAL) {
00197         badState(c, cmd);
00198         return false;
00199     }
00200     return true;
00201 }
00202 
00203 bool ClientManager::handle(AdcCommand::SUP, Entity& c, AdcCommand& cmd) throw() {
00204     if(!verifySUP(c, cmd)) {
00205         return false;
00206     }
00207 
00208     if(c.getState() == Entity::STATE_PROTOCOL) {
00209         enterIdentify(c, true);
00210     } else if(c.getState() != Entity::STATE_NORMAL) {
00211         badState(c, cmd);
00212         return false;
00213     }
00214     return true;
00215 }
00216 
00217 bool ClientManager::verifySUP(Entity& c, AdcCommand& cmd) throw() {
00218     c.updateSupports(cmd);
00219 
00220     if(!c.hasSupport(AdcCommand::toFourCC("BASE"))) {
00221         c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC,
00222             "This hub requires BASE support"));
00223         c.disconnect(Util::REASON_NO_BASE_SUPPORT);
00224         return false;
00225     }
00226 
00227     if(!c.hasSupport(AdcCommand::toFourCC("TIGR"))) {
00228         c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC,
00229             "This hub requires TIGR support"));
00230         c.disconnect(Util::REASON_NO_TIGR_SUPPORT);
00231         return false;
00232     }
00233 
00234     return true;
00235 }
00236 
00237 bool ClientManager::verifyINF(Entity& c, AdcCommand& cmd) throw() {
00238     Client* cc = dynamic_cast<Client*>(&c);
00239 
00240     if(cc) {
00241         if(!verifyIp(*cc, cmd))
00242             return false;
00243     }
00244 
00245     if(!verifyCID(c, cmd))
00246         return false;
00247 
00248     if(!verifyNick(c, cmd))
00249         return false;
00250     
00251     if(cmd.getParam("DE", 0, strtmp)) {
00252         if (!Util::validateCharset(strtmp, 32)){
00253             c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC, "Invalid character in description"));
00254             c.disconnect(Util::REASON_INVALID_DESCRIPTION);
00255             return false;
00256         }
00257     }
00258     c.updateFields(cmd);
00259     return true;
00260 }
00261 
00262 bool ClientManager::verifyPassword(Entity& c, const string& password, const ByteVector& salt,
00263     const string& suppliedHash) {
00264     TigerHash tiger;
00265     tiger.update(&password[0], password.size());
00266     tiger.update(&salt[0], salt.size());
00267     uint8_t tmp[TigerHash::BYTES];
00268     Encoder::fromBase32(suppliedHash.c_str(), tmp, TigerHash::BYTES);
00269     if(memcmp(tiger.finalize(), tmp, TigerHash::BYTES) == 0) {
00270         return true;
00271     }
00272 
00273     return false;
00274 }
00275 
00276 bool ClientManager::verifyOverflow(Entity& c) {
00277     size_t overflowing = 0;
00278     for(EntityIter i = entities.begin(), iend = entities.end(); i != iend; ++i) {
00279         if(i->second->getOverflow()) {
00280             overflowing++;
00281         }
00282     }
00283 
00284     if(overflowing > 3 && overflowing > (entities.size() / 4)) {
00285         c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_HUB_FULL, "Not enough bandwidth available, please try again later"));
00286         c.disconnect(Util::REASON_NO_BANDWIDTH);
00287         return false;
00288     }
00289 
00290     return true;
00291 }
00292 
00293 bool ClientManager::handle(AdcCommand::INF, Entity& c, AdcCommand& cmd) throw() {
00294     if(c.getState() != Entity::STATE_IDENTIFY && c.getState() != Entity::STATE_NORMAL) {
00295         badState(c, cmd);
00296         return false;
00297     }
00298 
00299     if(!verifyINF(c, cmd))
00300         return false;
00301 
00302     if(c.getState() == Entity::STATE_IDENTIFY) {
00303         if(!verifyOverflow(c)) {
00304             return false;
00305         }
00306 
00307         enterNormal(c, true, true);
00308         return false;
00309     }
00310 
00311     return true;
00312 }
00313 
00314 bool ClientManager::verifyIp(Client& c, AdcCommand& cmd) throw() {
00315     if(c.isSet(Entity::FLAG_OK_IP))
00316         return true;
00317 
00318     std::string ip;
00319     if(cmd.getParam("I4", 0, ip)) {
00320         dcdebug("%s verifying ip\n", AdcCommand::fromSID(c.getSID()).c_str());
00321         if(ip.empty() || ip == "0.0.0.0") {
00322             cmd.delParam("I4", 0);
00323             cmd.resetBuffer();
00324         } else if(ip != c.getIp() && !Util::isPrivateIp(c.getIp())) {
00325             c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_BAD_IP, "Your ip is " + c.getIp()).addParam(
00326                 "IP", c.getIp()));
00327             c.disconnect(Util::REASON_INVALID_IP);
00328             return false;
00329         } else
00330             return true;
00331     }
00332 
00333     if(!c.hasField("I4")) {
00334         c.setField("I4", c.getIp());
00335     }
00336     if(c.getState() != Entity::STATE_NORMAL) {
00337         cmd.addParam("I4", c.getIp());
00338         cmd.resetBuffer();
00339     }
00340 
00341     return true;
00342 }
00343 
00344 bool ClientManager::verifyCID(Entity& c, AdcCommand& cmd) throw() {
00345     if(cmd.getParam("ID", 0, strtmp)) {
00346         dcdebug("%s verifying CID\n", AdcCommand::fromSID(c.getSID()).c_str());
00347         if(c.getState() != Entity::STATE_IDENTIFY) {
00348             c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC, "CID changes not allowed"));
00349             c.disconnect(Util::REASON_CID_CHANGE);
00350             return false;
00351         }
00352 
00353         string spid;
00354         if(!cmd.getParam("PD", 0, spid)) {
00355             c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_INF_MISSING, "PID missing").addParam("FLPD"));
00356             c.disconnect(Util::REASON_PID_MISSING);
00357             return false;
00358         }
00359 
00360         if(strtmp.size() != CID::BASE32_SIZE || spid.size() != CID::BASE32_SIZE) {
00361             c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC, "Invalid CID/PID length"));
00362             c.disconnect(Util::REASON_PID_CID_LENGTH);
00363             return false;
00364         }
00365 
00366         CID cid(strtmp);
00367         CID pid(spid);
00368 
00369         TigerHash th;
00370         th.update(pid.data(), CID::SIZE);
00371         if(!(CID(th.finalize()) == cid)) {
00372             c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_INVALID_PID, "PID does not correspond to CID"));
00373             c.disconnect(Util::REASON_PID_CID_MISMATCH);
00374             return false;
00375         }
00376 
00377         Entity* other = getEntity(getSID(cid));
00378         if(other) {
00379             // disconnect the ghost
00380             removeEntity(*other);
00381             other->setFlag(Entity::FLAG_GHOST);
00382             other->send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_CID_TAKEN, "CID taken"));
00383             other->disconnect(Util::REASON_CID_TAKEN);
00384         }
00385 
00386         c.setCID(cid);
00387         cids.insert(make_pair(c.getCID(), &c));
00388         cmd.delParam("PD", 0);
00389     }
00390 
00391     if(cmd.getParam("PD", 0, strtmp)) {
00392         c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC, "CID required when sending PID"));
00393         c.disconnect(Util::REASON_PID_WITHOUT_CID);
00394         return false;
00395     }
00396 
00397     return true;
00398 }
00399 
00400 
00401 bool ClientManager::verifyNick(Entity& c, const AdcCommand& cmd) throw() {
00402     if(cmd.getParam("NI", 0, strtmp)) {
00403         dcdebug("%s verifying nick\n", AdcCommand::fromSID(c.getSID()).c_str());
00404 
00405         
00406         if (!Util::validateCharset(strtmp, 33)){
00407                 c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_NICK_INVALID, "Invalid character in nick"));
00408                 c.disconnect(Util::REASON_NICK_INVALID);
00409                 return false;
00410         }
00411 
00412         const string& oldNick = c.getField("NI");
00413         if(!oldNick.empty())
00414             nicks.erase(oldNick);
00415 
00416         if(nicks.find(strtmp) != nicks.end()) {
00417             c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_NICK_TAKEN,
00418                 "Nick taken, please pick another one"));
00419             c.disconnect(Util::REASON_NICK_TAKEN);
00420             return false;
00421         }
00422 
00423         nicks.insert(make_pair(strtmp, &c));
00424     }
00425 
00426     return true;
00427 }
00428 
00429 void ClientManager::setState(Entity& c, Entity::State newState) throw() {
00430     Entity::State oldState = c.getState();
00431     c.setState(newState);
00432     signalState_(c, oldState);
00433 }
00434 
00435 void ClientManager::enterIdentify(Entity& c, bool sendData) throw() {
00436     dcassert(c.getState() == Entity::STATE_PROTOCOL);
00437     dcdebug("%s entering IDENTIFY\n", AdcCommand::fromSID(c.getSID()).c_str());
00438     if(sendData) {
00439         c.send(hub.getSUP());
00440         c.send(AdcCommand(AdcCommand::CMD_SID).addParam(AdcCommand::fromSID(c.getSID())));
00441         c.send(hub.getINF());
00442     }
00443 
00444     setState(c, Entity::STATE_IDENTIFY);
00445 }
00446 
00447 ByteVector ClientManager::enterVerify(Entity& c, bool sendData) throw() {
00448     dcassert(c.getState() == Entity::STATE_IDENTIFY);
00449     dcdebug("%s entering VERIFY\n", AdcCommand::fromSID(c.getSID()).c_str());
00450 
00451     ByteVector challenge;
00452     challenge.reserve(32);
00453     for(int i = 0; i < 32 / 4; ++i) {
00454         uint32_t r = Util::rand();
00455         challenge.insert(challenge.end(), (uint8_t*) &r, 4 + (uint8_t*) &r);
00456     }
00457 
00458     if(sendData) {
00459         c.send(AdcCommand(AdcCommand::CMD_GPA).addParam(Encoder::toBase32(&challenge[0], challenge.size())));
00460     }
00461 
00462     setState(c, Entity::STATE_VERIFY);
00463     return challenge;
00464 }
00465 
00466 bool ClientManager::enterNormal(Entity& c, bool sendData, bool sendOwnInf) throw() {
00467     dcassert(c.getState() == Entity::STATE_IDENTIFY || c.getState() == Entity::STATE_VERIFY);
00468     dcdebug("%s entering NORMAL\n", AdcCommand::fromSID(c.getSID()).c_str());
00469 
00470     if(sendData) {
00471         for(EntityIter i = entities.begin(); i != entities.end(); ++i) {
00472             c.send(i->second->getINF());
00473         }
00474     }
00475 
00476     if(sendOwnInf) {
00477         sendToAll(c.getINF());
00478         if(sendData) {
00479             c.send(c.getINF());
00480         }
00481     }
00482 
00483     removeLogins(c);
00484 
00485     entities.insert(make_pair(c.getSID(), &c));
00486 
00487     setState(c, Entity::STATE_NORMAL);
00488     return true;
00489 }
00490 
00491 void ClientManager::removeLogins(Entity& e) throw() {
00492     Client* c = dynamic_cast<Client*>(&e);
00493     if(!c) {
00494         return;
00495     }
00496 
00497     auto i = find_if(logins.begin(), logins.end(), CompareFirst<Client*, uint32_t> (c));
00498     if(i != logins.end()) {
00499         logins.erase(i);
00500     }
00501 }
00502 
00503 void ClientManager::removeEntity(Entity& c) throw() {
00504     if(c.isSet(Entity::FLAG_GHOST))
00505         return;
00506 
00507     signalDisconnected_(c);
00508     dcdebug("Removing %s\n", AdcCommand::fromSID(c.getSID()).c_str());
00509     if(c.getState() == Entity::STATE_NORMAL) {
00510         entities.erase(c.getSID());
00511         sendToAll(AdcCommand(AdcCommand::CMD_QUI).addParam(AdcCommand::fromSID(c.getSID())).addParam("DI", "1").getBuffer());
00512     } else {
00513         removeLogins(c);
00514     }
00515 
00516     nicks.erase(c.getField("NI"));
00517     cids.erase(c.getCID());
00518 }
00519 
00520 Entity* ClientManager::getEntity(uint32_t aSid) throw() {
00521     if(aSid == AdcCommand::INVALID_SID) {
00522         dcdebug("Request for invalid SID\n");
00523         return 0;
00524     }
00525 
00526     if(aSid == AdcCommand::HUB_SID) {
00527         return &hub;
00528     }
00529 
00530     EntityIter i = entities.find(aSid);
00531     return (i == entities.end()) ? 0 : i->second;
00532 }
00533 
00534 uint32_t ClientManager::getSID(const string& aNick) const throw() {
00535     NickMap::const_iterator i = nicks.find(aNick);
00536     return (i == nicks.end()) ? 0 : i->second->getSID();
00537 }
00538 
00539 uint32_t ClientManager::getSID(const CID& cid) const throw() {
00540     CIDMap::const_iterator i = cids.find(cid);
00541     return (i == cids.end()) ? 0 : i->second->getSID();
00542 }
00543 
00544 void ClientManager::onFailed(Client& c, const boost::system::error_code& ec) throw() {
00545     dcdebug("%s failed: %d %s\n", AdcCommand::fromSID(c.getSID()).c_str(), ec.value(), ec.message().c_str());
00546 
00547     removeEntity(c);
00548 }
00549 
00550 }
Generated on Sat Nov 27 23:37:53 2010 for adchpp by  doxygen 1.6.3