protocol/service: Use block list for connections

The service learns to consult the policies database to check if a
`NodeId` is blocked.

When a node is blocked the service will prevent any inbound or
outbound connection.

In the case of an inbound connection, a disconnect must be submitted
to the reactor.

In the case of an outbound connection, nodes are filtered out from any
connection lists, and prevented from submitting a connect to the
reactor.
This commit is contained in:
Adrian Duke 2026-02-13 17:51:33 +00:00 committed by Lorenz Leutgeb
parent 94e0a5128d
commit 1fa14ef529
2 changed files with 22 additions and 0 deletions

View File

@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## New Features
- The block policy for `NodeId`'s is used for limiting the namespaces fetched
from other nodes. It is now also extended to block connections to the blocked
`NodeId`.
- The set of references returned by `references_of` were restricted to `heads`,
`tags`, `notes`, `rad`, and `cobs`. The restriction is lifted, and the only
references filtered out are `refs/tmp/heads` used by `radicle-remote-helper`

View File

@ -230,6 +230,8 @@ pub enum ConnectError {
"attempted connection to {nid}, via {addr} but addresses of this kind are not supported"
)]
UnsupportedAddress { nid: NodeId, addr: Address },
#[error("attempted connection with blocked peer {nid}")]
Blocked { nid: NodeId },
}
/// A store for all node data.
@ -1219,6 +1221,16 @@ where
}
pub fn connected(&mut self, remote: NodeId, addr: Address, link: Link) {
if let Ok(true) = self.policies.is_blocked(&remote) {
self.emitter.emit(Event::PeerDisconnected {
nid: remote,
reason: format!("{remote} is blocked"),
});
info!(target: "service", "Disconnecting blocked inbound peer {remote}");
self.outbox.disconnect(remote, DisconnectReason::Policy);
return;
}
info!(target: "service", "Connected to {remote} ({addr}) ({link:?})");
self.emitter.emit(Event::PeerConnected { nid: remote });
@ -2164,6 +2176,9 @@ where
if nid == self.node_id() {
return Err(ConnectError::SelfConnection);
}
if let Ok(true) = self.policies.is_blocked(&nid) {
return Err(ConnectError::Blocked { nid });
}
if !self.is_supported_address(&addr) {
return Err(ConnectError::UnsupportedAddress { nid, addr });
}
@ -2397,6 +2412,7 @@ where
.filter(|entry| !entry.address.banned)
.filter(|entry| !entry.penalty.is_connect_threshold_reached())
.filter(|entry| !self.sessions.contains_key(&entry.node))
.filter(|entry| !self.policies.is_blocked(&entry.node).unwrap_or(false))
.filter(|entry| !self.config.external_addresses.contains(&entry.address.addr))
.filter(|entry| &entry.node != self.nid())
.filter(|entry| self.is_supported_address(&entry.address.addr))
@ -2564,6 +2580,9 @@ where
for (nid, session) in self.sessions.iter_mut() {
if self.config.is_persistent(nid) {
if self.policies.is_blocked(nid).unwrap_or(false) {
continue;
}
if let session::State::Disconnected { retry_at, .. } = &mut session.state {
// TODO: Try to reconnect only if the peer was attempted. A disconnect without
// even a successful attempt means that we're unlikely to be able to reconnect.