00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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;
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:
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
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
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 }