/* */ #include "DHTBucket.h" #include #include #include #include "DHTNode.h" #include "LogFactory.h" #include "Logger.h" #include "util.h" #include "DHTConstants.h" #include "a2functional.h" #include "bittorrent_helper.h" #include "bitfield.h" namespace aria2 { DHTBucket::DHTBucket(size_t prefixLength, const unsigned char* max, const unsigned char* min, const SharedHandle& localNode): _prefixLength(prefixLength), _localNode(localNode), _logger(LogFactory::getInstance()) { memcpy(_max, max, DHT_ID_LENGTH); memcpy(_min, min, DHT_ID_LENGTH); } DHTBucket::DHTBucket(const SharedHandle& localNode): _prefixLength(0), _localNode(localNode), _logger(LogFactory::getInstance()) { memset(_max, 0xff, DHT_ID_LENGTH); memset(_min, 0, DHT_ID_LENGTH); } DHTBucket::~DHTBucket() {} void DHTBucket::getRandomNodeID(unsigned char* nodeID) const { if(_prefixLength == 0) { util::generateRandomKey(nodeID); } else { size_t lastByteIndex = (_prefixLength-1)/8; util::generateRandomKey(nodeID); memcpy(nodeID, _min, lastByteIndex+1); } } bool DHTBucket::isInRange(const SharedHandle& node) const { return isInRange(node->getID(), _max, _min); } bool DHTBucket::isInRange(const unsigned char* nodeID) const { return isInRange(nodeID, _max, _min); } // Returns true if nodeID is in [min, max] (inclusive). bool DHTBucket::isInRange(const unsigned char* nodeID, const unsigned char* max, const unsigned char* min) const { return !std::lexicographical_compare(&nodeID[0], &nodeID[DHT_ID_LENGTH], &min[0], &min[DHT_ID_LENGTH]) && !std::lexicographical_compare(&max[0], &max[DHT_ID_LENGTH], &nodeID[0], &nodeID[DHT_ID_LENGTH]); } bool DHTBucket::addNode(const SharedHandle& node) { notifyUpdate(); std::deque >::iterator itr = std::find(_nodes.begin(), _nodes.end(), node); if(itr == _nodes.end()) { if(_nodes.size() < K) { _nodes.push_back(node); return true; } else { if(_nodes.front()->isBad()) { _nodes.erase(_nodes.begin()); _nodes.push_back(node); return true; } else { return false; } } } else { _nodes.erase(itr); _nodes.push_back(node); return true; } } void DHTBucket::cacheNode(const SharedHandle& node) { // _cachedNodes are sorted by last time seen _cachedNodes.push_front(node); if(_cachedNodes.size() > CACHE_SIZE) { _cachedNodes.resize(CACHE_SIZE, SharedHandle()); } } void DHTBucket::dropNode(const SharedHandle& node) { if(_cachedNodes.size()) { std::deque >::iterator itr = find(_nodes.begin(), _nodes.end(), node); if(itr != _nodes.end()) { _nodes.erase(itr); _nodes.push_back(_cachedNodes.front()); _cachedNodes.erase(_cachedNodes.begin()); } } } void DHTBucket::moveToHead(const SharedHandle& node) { std::deque >::iterator itr = std::find(_nodes.begin(), _nodes.end(), node); if(itr != _nodes.end()) { _nodes.erase(itr); _nodes.push_front(node); } } void DHTBucket::moveToTail(const SharedHandle& node) { std::deque >::iterator itr = std::find(_nodes.begin(), _nodes.end(), node); if(itr != _nodes.end()) { _nodes.erase(itr); _nodes.push_back(node); } } bool DHTBucket::splitAllowed() const { return _prefixLength < DHT_ID_LENGTH*8-1 && isInRange(_localNode); } SharedHandle DHTBucket::split() { assert(splitAllowed()); unsigned char rMax[DHT_ID_LENGTH]; memcpy(rMax, _max, DHT_ID_LENGTH); bitfield::flipBit(rMax, DHT_ID_LENGTH, _prefixLength); unsigned char rMin[DHT_ID_LENGTH]; memcpy(rMin, _min, DHT_ID_LENGTH); bitfield::flipBit(_min, DHT_ID_LENGTH, _prefixLength); ++_prefixLength; SharedHandle rBucket(new DHTBucket(_prefixLength, rMax, rMin, _localNode)); std::deque > lNodes; for(std::deque >::iterator i = _nodes.begin(), eoi = _nodes.end(); i != eoi; ++i) { if(rBucket->isInRange(*i)) { assert(rBucket->addNode(*i)); } else { lNodes.push_back(*i); } } _nodes = lNodes; // TODO create toString() and use it. if(_logger->debug()) { _logger->debug("New bucket. prefixLength=%u, Range:%s-%s", static_cast(rBucket->getPrefixLength()), util::toHex(rBucket->getMinID(), DHT_ID_LENGTH).c_str(), util::toHex(rBucket->getMaxID(), DHT_ID_LENGTH).c_str()); _logger->debug("Existing bucket. prefixLength=%u, Range:%s-%s", static_cast(_prefixLength), util::toHex(getMinID(), DHT_ID_LENGTH).c_str(), util::toHex(getMaxID(), DHT_ID_LENGTH).c_str()); } return rBucket; } void DHTBucket::getGoodNodes (std::vector >& goodNodes) const { goodNodes.insert(goodNodes.end(), _nodes.begin(), _nodes.end()); goodNodes.erase(std::remove_if(goodNodes.begin(), goodNodes.end(), mem_fun_sh(&DHTNode::isBad)), goodNodes.end()); } SharedHandle DHTBucket::getNode(const unsigned char* nodeID, const std::string& ipaddr, uint16_t port) const { SharedHandle node(new DHTNode(nodeID)); node->setIPAddress(ipaddr); node->setPort(port); std::deque >::const_iterator itr = std::find(_nodes.begin(), _nodes.end(), node); if(itr == _nodes.end()) { return SharedHandle(); } else { return *itr; } } bool DHTBucket::operator==(const DHTBucket& bucket) const { return memcmp(_max, bucket._max, DHT_ID_LENGTH) == 0 && memcmp(_min, bucket._min, DHT_ID_LENGTH) == 0; } bool DHTBucket::needsRefresh() const { return _nodes.size() < K || _lastUpdated.elapsed(DHT_BUCKET_REFRESH_INTERVAL); } void DHTBucket::notifyUpdate() { _lastUpdated.reset(); } class FindQuestionableNode { public: bool operator()(const SharedHandle& node) const { return node->isQuestionable(); } }; bool DHTBucket::containsQuestionableNode() const { return std::find_if(_nodes.begin(), _nodes.end(), FindQuestionableNode()) != _nodes.end(); } SharedHandle DHTBucket::getLRUQuestionableNode() const { std::deque >::const_iterator i = std::find_if(_nodes.begin(), _nodes.end(), FindQuestionableNode()); if(i == _nodes.end()) { return SharedHandle(); } else { return *i; } } } // namespace aria2