Skip to content

Commit e9325d3

Browse files
committed
impl Display for SocketAddress
1 parent 22a0bfc commit e9325d3

File tree

2 files changed

+94
-19
lines changed

2 files changed

+94
-19
lines changed

lightning/src/ln/msgs.rs

+87-19
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use core::fmt;
4545
use core::fmt::Debug;
4646
use core::ops::Deref;
4747
#[cfg(feature = "std")]
48-
use core::str::FromStr;
48+
use core::{fmt::Display, str::FromStr};
4949
use crate::io::{self, Cursor, Read};
5050
use crate::io_extras::read_to_end;
5151

@@ -918,6 +918,8 @@ pub enum SocketAddressParseError {
918918
InvalidInput,
919919
/// Invalid port
920920
InvalidPort,
921+
/// Invalid onion v2 address
922+
InvalidOnionV2,
921923
/// Invalid onion v3 address
922924
InvalidOnionV3,
923925
}
@@ -929,6 +931,7 @@ impl fmt::Display for SocketAddressParseError {
929931
SocketAddressParseError::InvalidInput => write!(f, "Invalid input format. \
930932
Expected: \"<ipv4>:<port>\", \"[<ipv6>]:<port>\", \"<onion address>.onion:<port>\" or \"<hostname>:<port>\""),
931933
SocketAddressParseError::InvalidPort => write!(f, "Invalid port"),
934+
SocketAddressParseError::InvalidOnionV2 => write!(f, "Invalid onion v2 address"),
932935
SocketAddressParseError::InvalidOnionV3 => write!(f, "Invalid onion v3 address"),
933936
}
934937
}
@@ -958,16 +961,37 @@ impl From<std::net::SocketAddr> for SocketAddress {
958961
}
959962
}
960963

964+
/// Parses an OnionV2 host and port into a [`SocketAddress::OnionV2`].
965+
///
966+
/// The host part must end with ".onion".
967+
pub fn parse_onion_v2_address(host: &str) -> Result<SocketAddress, SocketAddressParseError> {
968+
if host.ends_with(".onion") {
969+
let domain = &host[..host.len() - ".onion".len()];
970+
if domain.len() != 16 {
971+
return Err(SocketAddressParseError::InvalidOnionV2);
972+
}
973+
let onion = base32::Alphabet::RFC4648 { padding: false }.decode(&domain).map_err(|_| SocketAddressParseError::InvalidOnionV2)?;
974+
if onion.len() != 12 {
975+
return Err(SocketAddressParseError::InvalidOnionV2);
976+
}
977+
let mut bytes = [0; 12];
978+
bytes.copy_from_slice(&onion);
979+
return Ok(SocketAddress::OnionV2(bytes));
980+
} else {
981+
return Err(SocketAddressParseError::InvalidInput);
982+
}
983+
}
984+
961985
/// Parses an OnionV3 host and port into a [`SocketAddress::OnionV3`].
962986
///
963987
/// The host part must end with ".onion".
964-
pub fn parse_onion_address(host: &str, port: u16) -> Result<SocketAddress, SocketAddressParseError> {
988+
pub fn parse_onion_v3_address(host: &str, port: u16) -> Result<SocketAddress, SocketAddressParseError> {
965989
if host.ends_with(".onion") {
966990
let domain = &host[..host.len() - ".onion".len()];
967991
if domain.len() != 56 {
968992
return Err(SocketAddressParseError::InvalidOnionV3);
969993
}
970-
let onion = base32::Alphabet::RFC4648 { padding: false }.decode(&domain).map_err(|_| SocketAddressParseError::InvalidOnionV3)?;
994+
let onion = base32::Alphabet::RFC4648 { padding: false }.decode(&domain).map_err(|_| SocketAddressParseError::InvalidOnionV3)?;
971995
if onion.len() != 35 {
972996
return Err(SocketAddressParseError::InvalidOnionV3);
973997
}
@@ -984,6 +1008,40 @@ pub fn parse_onion_address(host: &str, port: u16) -> Result<SocketAddress, Socke
9841008
}
9851009
}
9861010

1011+
/// [`SocketAddress::OnionV2`] to onion address string
1012+
pub fn to_onion_v2_string(bytes: &[u8; 12]) -> String {
1013+
let onion = base32::Alphabet::RFC4648 { padding: false }.encode(&bytes[..]);
1014+
format!("{}.onion", onion)
1015+
}
1016+
1017+
/// [`SocketAddress::OnionV3`] to onion address string
1018+
pub fn to_onion_v3_string(key: &[u8; 32], checksum: &u16, version: &u8, port: &u16) -> String{
1019+
let [first_checksum_flag, second_checksum_flag] = checksum.to_be_bytes();
1020+
let mut addr = vec![*version, first_checksum_flag, second_checksum_flag];
1021+
addr.extend_from_slice(key);
1022+
let onion = base32::Alphabet::RFC4648 { padding: false }.encode(&addr);
1023+
format!("{}.onion:{}", onion, port)
1024+
}
1025+
1026+
#[cfg(feature = "std")]
1027+
impl Display for SocketAddress {
1028+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1029+
match self {
1030+
SocketAddress::TcpIpV4{addr, port} => write!(f, "{}:{}", std::net::Ipv4Addr::from(*addr), port)?,
1031+
SocketAddress::TcpIpV6{addr, port} => write!(f, "[{}]:{}", std::net::Ipv6Addr::from(*addr), port)?,
1032+
SocketAddress::OnionV2(bytes) => write!(f, "{}", to_onion_v2_string(&bytes))?,
1033+
SocketAddress::OnionV3 {
1034+
ed25519_pubkey,
1035+
checksum,
1036+
version,
1037+
port,
1038+
} => write!(f, "{}", to_onion_v3_string(&ed25519_pubkey, checksum, version, port))?,
1039+
SocketAddress::Hostname { hostname, port } => write!(f, "{}:{}", hostname, port)?,
1040+
}
1041+
Ok(())
1042+
}
1043+
}
1044+
9871045
#[cfg(feature = "std")]
9881046
impl FromStr for SocketAddress {
9891047
type Err = SocketAddressParseError;
@@ -994,12 +1052,13 @@ impl FromStr for SocketAddress {
9941052
Err(_) => {
9951053
let trimmed_input = match s.rfind(":") {
9961054
Some(pos) => pos,
997-
None => return Err(SocketAddressParseError::InvalidInput),
1055+
None if s.ends_with(".onion") => return parse_onion_v2_address(s),
1056+
_ => return Err(SocketAddressParseError::InvalidInput),
9981057
};
9991058
let host = &s[..trimmed_input];
10001059
let port: u16 = s[trimmed_input + 1..].parse().map_err(|_| SocketAddressParseError::InvalidPort)?;
10011060
if host.ends_with(".onion") {
1002-
return parse_onion_address(host, port);
1061+
return parse_onion_v3_address(host, port);
10031062
};
10041063
if let Ok(hostname) = Hostname::try_from(s[..trimmed_input].to_string()) {
10051064
return Ok(SocketAddress::Hostname { hostname, port });
@@ -4066,32 +4125,41 @@ mod tests {
40664125
#[test]
40674126
#[cfg(feature = "std")]
40684127
fn test_socket_address_from_str() {
4069-
assert_eq!(SocketAddress::TcpIpV4 {
4128+
let v4 = SocketAddress::TcpIpV4 {
40704129
addr: Ipv4Addr::new(127, 0, 0, 1).octets(),
40714130
port: 1234,
4072-
}, SocketAddress::from_str("127.0.0.1:1234").unwrap());
4131+
};
4132+
assert_eq!(v4, SocketAddress::from_str("127.0.0.1:1234").unwrap());
4133+
assert_eq!(v4, SocketAddress::from_str(&v4.to_string()).unwrap());
40734134

4074-
assert_eq!(SocketAddress::TcpIpV6 {
4135+
let v6 = SocketAddress::TcpIpV6 {
40754136
addr: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).octets(),
40764137
port: 1234,
4077-
}, SocketAddress::from_str("[0:0:0:0:0:0:0:1]:1234").unwrap());
4078-
assert_eq!(
4079-
SocketAddress::Hostname {
4138+
};
4139+
assert_eq!(v6, SocketAddress::from_str("[0:0:0:0:0:0:0:1]:1234").unwrap());
4140+
assert_eq!(v6, SocketAddress::from_str(&v6.to_string()).unwrap());
4141+
4142+
let hostname = SocketAddress::Hostname {
40804143
hostname: Hostname::try_from("lightning-node.mydomain.com".to_string()).unwrap(),
40814144
port: 1234,
4082-
}, SocketAddress::from_str("lightning-node.mydomain.com:1234").unwrap());
4083-
assert_eq!(
4084-
SocketAddress::Hostname {
4085-
hostname: Hostname::try_from("example.com".to_string()).unwrap(),
4086-
port: 1234,
4087-
}, SocketAddress::from_str("example.com:1234").unwrap());
4088-
assert_eq!(SocketAddress::OnionV3 {
4145+
};
4146+
assert_eq!(hostname, SocketAddress::from_str("lightning-node.mydomain.com:1234").unwrap());
4147+
assert_eq!(hostname, SocketAddress::from_str(&hostname.to_string()).unwrap());
4148+
4149+
let v2 = SocketAddress::OnionV2 ([37, 24, 75, 5, 25, 73, 117, 194, 139, 102, 182, 107]);
4150+
assert_eq!(v2, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap());
4151+
assert_eq!(v2, SocketAddress::from_str(&v2.to_string()).unwrap());
4152+
4153+
let v3 = SocketAddress::OnionV3 {
40894154
ed25519_pubkey: [37, 24, 75, 5, 25, 73, 117, 194, 139, 102, 182, 107, 4, 105, 247, 246, 85,
40904155
111, 177, 172, 49, 137, 167, 155, 64, 221, 163, 47, 31, 33, 71, 3],
40914156
checksum: 48326,
40924157
version: 121,
40934158
port: 1234
4094-
}, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap());
4159+
};
4160+
assert_eq!(v3, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap());
4161+
assert_eq!(v3, SocketAddress::from_str(&v3.to_string()).unwrap());
4162+
40954163
assert_eq!(Err(SocketAddressParseError::InvalidOnionV3), SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6.onion:1234"));
40964164
assert_eq!(Err(SocketAddressParseError::InvalidInput), SocketAddress::from_str("127.0.0.1@1234"));
40974165
assert_eq!(Err(SocketAddressParseError::InvalidInput), "".parse::<SocketAddress>());

lightning/src/util/ser.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,13 @@ impl Hostname {
12971297
(&self.0).len() as u8
12981298
}
12991299
}
1300+
#[cfg(feature = "std")]
1301+
impl core::fmt::Display for Hostname {
1302+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1303+
write!(f, "{}", self.0)?;
1304+
Ok(())
1305+
}
1306+
}
13001307
impl Deref for Hostname {
13011308
type Target = String;
13021309

0 commit comments

Comments
 (0)