node: Explicit default for `AddressConfig`

The intent to drop outgoing connections is modeled as `Option::None`
which is brittle and easy to miss.

Extend `enum AddressConfig` with a variant that is more explicit.
This commit is contained in:
Lorenz Leutgeb 2026-02-15 13:13:04 +01:00
parent fb1808395e
commit 10a82958ca
No known key found for this signature in database
4 changed files with 54 additions and 16 deletions

View File

@ -247,14 +247,7 @@ $ rad config schema
},
"onion": {
"description": "Onion address config.",
"anyOf": [
{
"$ref": "#/$defs/AddressConfig"
},
{
"type": "null"
}
]
"$ref": "#/$defs/AddressConfig"
},
"network": {
"description": "Peer-to-peer network.",
@ -410,6 +403,19 @@ $ rad config schema
"required": [
"mode"
]
},
{
"description": "Drop connections to this address type.",
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "drop"
}
},
"required": [
"mode"
]
}
]
},

View File

@ -1096,10 +1096,10 @@ pub fn dial<G: Ecdh<Pk = NodeId>>(
(HostName::Tor(onion), proxy) => match config.onion {
// In onion proxy mode, simply use the configured proxy address.
// This takes precedence over any global proxy.
Some(AddressConfig::Proxy { address }) => address.into(),
AddressConfig::Proxy { address } => address.into(),
// In "forward" mode, if a global proxy is set, we use that, otherwise
// we treat `.onion` addresses as regular DNS names.
Some(AddressConfig::Forward) => {
AddressConfig::Forward => {
if let Some(proxy) = proxy {
proxy.into()
} else {
@ -1107,7 +1107,7 @@ pub fn dial<G: Ecdh<Pk = NodeId>>(
}
}
// If onion address support isn't configured, refuse to connect.
None => {
AddressConfig::Drop => {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"no configuration found for .onion addresses",

View File

@ -30,6 +30,8 @@ use radicle::node;
use radicle::node::address;
use radicle::node::address::Store as _;
use radicle::node::address::{AddressBook, AddressType, KnownAddress};
#[cfg(feature = "tor")]
use radicle::node::config::AddressConfig;
use radicle::node::config::{PeerConfig, RateLimit};
use radicle::node::device::Device;
use radicle::node::refs::Store as _;
@ -2656,7 +2658,7 @@ where
match AddressType::from(address) {
// Only consider onion addresses if configured.
#[cfg(feature = "tor")]
AddressType::Onion => self.config.onion.is_some(),
AddressType::Onion => self.config.onion != AddressConfig::Drop,
AddressType::Dns | AddressType::Ipv4 | AddressType::Ipv6 => true,
}
}

View File

@ -355,7 +355,7 @@ pub enum Relay {
}
/// Proxy configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "mode")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg(feature = "tor")]
@ -368,6 +368,9 @@ pub enum AddressConfig {
/// Forward address to the next layer. Either this is the global proxy,
/// or the operating system, via DNS.
Forward,
/// Drop connections to this address type.
#[default]
Drop,
}
/// Default seeding policy. Applies when no repository policies for the given repo are found.
@ -546,8 +549,12 @@ pub struct Config {
pub proxy: Option<net::SocketAddr>,
/// Onion address config.
#[cfg(feature = "tor")]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub onion: Option<AddressConfig>,
#[serde(
default,
skip_serializing_if = "crate::serde_ext::is_default",
deserialize_with = "crate::serde_ext::null_to_default"
)]
pub onion: AddressConfig,
/// Peer-to-peer network.
#[serde(default)]
pub network: Network,
@ -603,7 +610,7 @@ impl Config {
network: Network::default(),
proxy: None,
#[cfg(feature = "tor")]
onion: None,
onion: AddressConfig::Drop,
relay: Relay::default(),
limits: Limits::default(),
workers: Workers::default(),
@ -944,4 +951,27 @@ mod test {
crate::storage::refs::FeatureLevel::Parent
);
}
#[cfg(feature = "tor")]
#[test]
fn onion_absent() {
let actual: super::Config = serde_json::from_value(json!({
"alias": "radicle",
}))
.unwrap();
assert_eq!(super::AddressConfig::Drop, actual.onion);
}
#[cfg(feature = "tor")]
#[test]
fn onion_null() {
// Backwards compatibility: Prior versions allowed to set `onion` to `null`,
// which should be treated the same as the default, i.e. `Drop`.
let actual: super::Config = serde_json::from_value(json!({
"alias": "radicle",
"onion": null,
}))
.unwrap();
assert_eq!(super::AddressConfig::Drop, actual.onion);
}
}