radicle/node: Do not error on IPv6 without `[]`

IPv6 addresses are already persisted in `config.json` files.
The parsing of these old addresses, e.g. `FE80::0202:B3FF:FE1E:8329:5976` should remain valid.
Emit a warning when they are found.
The output of the address will still enclose the host in `[]`.
This commit is contained in:
Fintan Halpenny 2026-03-19 09:38:12 +00:00 committed by Lorenz Leutgeb
parent c237a3fe47
commit d01ff2e79d
3 changed files with 47 additions and 6 deletions

View File

@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## Fixed Bugs
- The fix to ambiguous IPv6 addresses, e.g. `::1:8776` vs. `[::1]:8776`,
resulted in backward incompatibility. Configuration files containing addresses
in the ambiguous format could not be parsed anymore. Partially undo this change
to stay backward compatible, but log a warning in case ambiguous addresses are
encountered.
## 1.7.0
## Release Highlights
- The "Signed References" feature was reimplemented. The commits in

View File

@ -548,10 +548,13 @@ impl FromStr for Address {
{
HostName::Ip(host.parse::<Ipv6Addr>()?.into())
} else {
// Require IPv6 addresses to always be enclosed in `[` and `]`.
host.parse().and_then(|host| match host {
HostName::Ip(IpAddr::V6(_)) => Err(AddrParseError::UnknownAddressFormat),
host => Ok(host),
// Warn on IPv6 addresses that are not enclosed in `[` and `]`.
host.parse().map(|host| match host {
HostName::Ip(IpAddr::V6(addr)) => {
log::warn!("Address format will change in the future. '{s}' should be changed to '[{addr}]:{port}' to stay compatible. Refer to RFC 5926, Sec. 6 as well as RFC 3986, Sec. D.1. and RFC 2732, Sec. 2.");
host
},
host => host,
})?
};
@ -1525,6 +1528,7 @@ mod test {
assert!(Address::from_str("[::1]:8776").is_ok());
assert!(Address::from_str("[::ffff:127.0.0.1]:8776").is_ok());
assert!(Address::from_str("localhost:8776").is_ok());
assert!(Address::from_str("::1:8776").is_ok()); // Backwards-compatibility
assert!(Address::from_str("").is_err());
assert!(Address::from_str(":").is_err());
@ -1532,7 +1536,6 @@ mod test {
assert!(Address::from_str("127.0.0.1:xyz").is_err());
assert!(Address::from_str("[invalid]:8776").is_err());
assert!(Address::from_str("[127.0.0.1]:8776").is_err());
assert!(Address::from_str("::1:8776").is_err());
}
#[test]

View File

@ -712,7 +712,7 @@ wrapper!(
#[allow(clippy::unwrap_used)]
mod test {
use super::{DefaultSeedingPolicy, Scope};
use crate::node::policy;
use crate::node::{policy, Alias};
use serde_json::json;
#[test]
@ -833,4 +833,32 @@ mod test {
.unwrap()
);
}
#[test]
fn regression_ipv6_address_brackets() {
let address = "[2001:db8::1]:5976".to_string();
let config = json!({
"alias": "radicle",
"externalAddresses": [address],
});
let got: super::Config = serde_json::from_value(config).unwrap();
let mut expected = super::Config::new(Alias::new("radicle"));
expected.external_addresses = vec![address.parse().unwrap()];
assert_eq!(got.alias, expected.alias);
assert_eq!(got.external_addresses, expected.external_addresses);
}
#[test]
fn regression_ipv6_address_no_brackets() {
let address = "2001:db8::1:5976".to_string();
let config = json!({
"alias": "radicle",
"externalAddresses": [address],
});
let got: super::Config = serde_json::from_value(config).unwrap();
let mut expected = super::Config::new(Alias::new("radicle"));
expected.external_addresses = vec![address.parse().unwrap()];
assert_eq!(got.alias, expected.alias);
assert_eq!(got.external_addresses, expected.external_addresses);
}
}