/* * Remove dead nodes */ bool KBucket::checkExpiration(uint64_t currentTime) { bool dirty = false; // we should ping oldest node from every bucket here // but since we have only one bucket now, simulate it by pinging more nodes unsigned int pingCount = max(K, min((int)2 * K, (int)(nodes.size() / (K * 10)) + 1)); // <-- pings 10 - 20 oldest nodes unsigned int pinged = 0; dcdrun(unsigned int removed = 0); // first, remove dead nodes NodeList::iterator i = nodes.begin(); while(i != nodes.end()) { Node::Ptr& node = *i; if(node->getType() == 4 && node->expires > 0 && node->expires <= currentTime) { if(node->unique(2)) { // node is dead, remove it string ip = node->getIdentity().getIp(); string port = node->getIdentity().getUdpPort(); ipMap.erase(ip + ":" + port); if(node->isOnline()) { ClientManager::getInstance()->putOffline(node.get()); node->dec(); } i = nodes.erase(i); dirty = true; dcdrun(removed++); } else { ++i; } continue; } if(node->expires == 0) node->expires = currentTime; // select the oldest expired node if(pinged < pingCount && node->getType() < 4 && node->expires <= currentTime) { // ping the oldest (expired) node node->setTimeout(currentTime); DHT::getInstance()->info(node->getIdentity().getIp(), static_cast<uint16_t>(Util::toInt(node->getIdentity().getUdpPort())), DHT::PING, node->getUser()->getCID(), node->getUdpKey()); pinged++; } ++i; } #ifdef _DEBUG int verified = 0; int types[5] = { 0 }; for(NodeList::const_iterator j = nodes.begin(); j != nodes.end(); j++) { Node::Ptr n = *j; if(n->isIpVerified()) verified++; dcassert(n->getType() >= 0 && n->getType() <= 4); types[n->getType()]++; } dcdebug("DHT Nodes: %d (%d verified), Types: %d/%d/%d/%d/%d, pinged %d of %d, removed %d\n", nodes.size(), verified, types[0], types[1], types[2], types[3], types[4], pinged, pingCount, removed); #endif return dirty; }
/* * Creates new (or update existing) node which is NOT added to our routing table */ Node::Ptr KBucket::createNode(const UserPtr& u, const string& ip, uint16_t port, bool update, bool isUdpKeyValid) { if(u->isSet(User::DHT)) // is this user already known in DHT? { Node::Ptr node = NULL; // no online node found, try get from routing table for(NodeList::iterator it = nodes.begin(); it != nodes.end(); ++it) { if(u->getCID() == (*it)->getUser()->getCID()) { node = *it; // put node at the end of the list nodes.erase(it); nodes.push_back(node); break; } } if(node == NULL && u->isOnline()) { // try to get node from ClientManager (user can be online but not in our routing table) // this fixes the bug with DHT node online twice node = (Node*)ClientManager::getInstance()->findDHTNode(u->getCID()); node = node.get(); } if(node != NULL) { // fine, node found, update it and return it if(update) { string oldIp = node->getIdentity().getIp(); string oldPort = node->getIdentity().getUdpPort(); if(ip != oldIp || static_cast<uint16_t>(Util::toInt(oldPort)) != port) { node->setIpVerified(false); // TODO: don't allow update when new IP already exists for different node // erase old IP and remember new one ipMap.erase(oldIp + ":" + oldPort); ipMap.insert(ip + ":" + Util::toString(port)); } if(!node->isIpVerified()) node->setIpVerified(isUdpKeyValid); node->setAlive(); node->getIdentity().setIp(ip); node->getIdentity().setUdpPort(Util::toString(port)); DHT::getInstance()->setDirty(); } return node; } } u->setFlag(User::DHT); Node::Ptr node(new Node(u)); node->getIdentity().setIp(ip); node->getIdentity().setUdpPort(Util::toString(port)); node->setIpVerified(isUdpKeyValid); return node; }