diff --git a/include/opendht/node.h b/include/opendht/node.h index 5ecb9a61e1e4853e2e01605840a26a409f79db99..14e8ae7c1929243ac9c026a5b3365680a5e9a3dc 100644 --- a/include/opendht/node.h +++ b/include/opendht/node.h @@ -50,6 +50,18 @@ struct Node { std::string getAddrStr() const { return addr.toString(); } + + /** + * Makes notice about an additionnal authentication error with this node. Up + * to MAX_AUTH_ERRORS errors are accepted in order to let the node recover. + * Upon this limit, the node expires. + */ + void authError() { + if (++auth_errors > MAX_AUTH_ERRORS) + setExpired(); + } + void authSuccess() { auth_errors = 0; } + bool isExpired() const { return expired_; } bool isGood(time_point now) const; bool isPendingMessage() const; @@ -83,7 +95,11 @@ struct Node { static constexpr const std::chrono::seconds MAX_RESPONSE_TIME {1}; private: + /* Number of times we accept authentication errors from this node. */ + static const constexpr unsigned MAX_AUTH_ERRORS {3}; + std::list<std::weak_ptr<Request>> requests_ {}; + unsigned auth_errors {0}; bool expired_ {false}; void clearPendingQueue() { diff --git a/src/dht.cpp b/src/dht.cpp index e7f9f803dba0c7f836778a567fa0ade9b25a4881..c1550185a11af508805935f093d3a096f5d5ac38 100644 --- a/src/dht.cpp +++ b/src/dht.cpp @@ -408,9 +408,9 @@ struct Dht::SearchNode { or not gs->second or not gs->second->pending())) return time_point::min(); return ((gs != getStatus.cend() and gs->second and gs->second->pending()) - or ack == acked.cend() or not ack->second or ack->second->pending()) ? - time_point::max() : - ack->second->reply_time + type.expiration - REANNOUNCE_MARGIN; + or ack == acked.cend() or not ack->second or ack->second->pending()) + ? time_point::max() + : ack->second->reply_time + type.expiration - REANNOUNCE_MARGIN; } /** @@ -3000,6 +3000,7 @@ void Dht::onError(std::shared_ptr<Request> req, DhtProtocolException e) { if (e.getCode() == DhtProtocolException::UNAUTHORIZED) { DHT_LOG.ERR("[node %s] token flush", req->node->toString().c_str()); + req->node->authError(); network_engine.cancelRequest(req); for (auto& srp : req->node->getFamily() == AF_INET ? searches4 : searches6) { auto& sr = srp.second; diff --git a/src/network_engine.cpp b/src/network_engine.cpp index f0e8c4012392825d605fed68f7db159760bb2c72..4ada3962fb26832344bfcb82898abff14b692a82 100644 --- a/src/network_engine.cpp +++ b/src/network_engine.cpp @@ -377,6 +377,9 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const SockAddr& break; } case MessageType::Reply: + if (msg.type == MessageType::AnnounceValue or msg.type == MessageType::Listen) + req->node->authSuccess(); + // erase before calling callback to make sure iterator is still valid if (not req->persistent) requests.erase(reqp);