use crate::ln::msgs::DecodeError;
use crate::util::{
base32,
ser::{Hostname, Readable, Writeable, Writer},
};
use std::fmt::Display;
use std::io::{self, Read};
use std::str::FromStr;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum SocketAddress {
TcpIpV4 {
addr: [u8; 4],
port: u16,
},
TcpIpV6 {
addr: [u8; 16],
port: u16,
},
OnionV2([u8; 12]),
OnionV3 {
ed25519_pubkey: [u8; 32],
checksum: u16,
version: u8,
port: u16,
},
Hostname {
hostname: Hostname,
port: u16,
},
}
impl SocketAddress {
pub const MAX_LEN: u16 = 258;
pub fn is_tor(&self) -> bool {
match self {
SocketAddress::TcpIpV4 { .. } => false,
SocketAddress::TcpIpV6 { .. } => false,
SocketAddress::OnionV2(_) => true,
SocketAddress::OnionV3 { .. } => true,
SocketAddress::Hostname { .. } => false,
}
}
}
impl Writeable for SocketAddress {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
match self {
SocketAddress::TcpIpV4 { addr, port } => {
1u8.write(writer)?;
addr.write(writer)?;
port.write(writer)?;
}
SocketAddress::TcpIpV6 { addr, port } => {
2u8.write(writer)?;
addr.write(writer)?;
port.write(writer)?;
}
SocketAddress::OnionV2(bytes) => {
3u8.write(writer)?;
bytes.write(writer)?;
}
SocketAddress::OnionV3 {
ed25519_pubkey,
checksum,
version,
port,
} => {
4u8.write(writer)?;
ed25519_pubkey.write(writer)?;
checksum.write(writer)?;
version.write(writer)?;
port.write(writer)?;
}
SocketAddress::Hostname { hostname, port } => {
5u8.write(writer)?;
hostname.write(writer)?;
port.write(writer)?;
}
}
Ok(())
}
}
impl Readable for Result<SocketAddress, u8> {
fn read<R: Read>(
reader: &mut R,
) -> Result<Result<SocketAddress, u8>, crate::ln::msgs::DecodeError> {
let byte = <u8 as Readable>::read(reader)?;
match byte {
1 => Ok(Ok(SocketAddress::TcpIpV4 {
addr: Readable::read(reader)?,
port: Readable::read(reader)?,
})),
2 => Ok(Ok(SocketAddress::TcpIpV6 {
addr: Readable::read(reader)?,
port: Readable::read(reader)?,
})),
3 => Ok(Ok(SocketAddress::OnionV2(Readable::read(reader)?))),
4 => Ok(Ok(SocketAddress::OnionV3 {
ed25519_pubkey: Readable::read(reader)?,
checksum: Readable::read(reader)?,
version: Readable::read(reader)?,
port: Readable::read(reader)?,
})),
5 => Ok(Ok(SocketAddress::Hostname {
hostname: Readable::read(reader)?,
port: Readable::read(reader)?,
})),
_ => Ok(Err(byte)),
}
}
}
impl Readable for SocketAddress {
fn read<R: Read>(reader: &mut R) -> Result<SocketAddress, DecodeError> {
match Readable::read(reader) {
Ok(Ok(res)) => Ok(res),
Ok(Err(_)) => Err(DecodeError::UnknownVersion),
Err(e) => Err(e),
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum SocketAddressParseError {
SocketAddrParse,
InvalidInput,
InvalidPort,
InvalidOnionV3,
}
impl std::fmt::Display for SocketAddressParseError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
SocketAddressParseError::SocketAddrParse => {
write!(f, "Socket address (IPv4/IPv6) parsing error")
}
SocketAddressParseError::InvalidInput => write!(
f,
"Invalid input format. \
Expected: \"<ipv4>:<port>\", \"[<ipv6>]:<port>\", \"<onion address>.onion:<port>\" or \"<hostname>:<port>\""
),
SocketAddressParseError::InvalidPort => write!(f, "Invalid port"),
SocketAddressParseError::InvalidOnionV3 => write!(f, "Invalid onion v3 address"),
}
}
}
impl From<std::net::SocketAddrV4> for SocketAddress {
fn from(addr: std::net::SocketAddrV4) -> Self {
SocketAddress::TcpIpV4 {
addr: addr.ip().octets(),
port: addr.port(),
}
}
}
impl From<std::net::SocketAddrV6> for SocketAddress {
fn from(addr: std::net::SocketAddrV6) -> Self {
SocketAddress::TcpIpV6 {
addr: addr.ip().octets(),
port: addr.port(),
}
}
}
impl From<std::net::SocketAddr> for SocketAddress {
fn from(addr: std::net::SocketAddr) -> Self {
match addr {
std::net::SocketAddr::V4(addr) => addr.into(),
std::net::SocketAddr::V6(addr) => addr.into(),
}
}
}
impl std::net::ToSocketAddrs for SocketAddress {
type Iter = std::vec::IntoIter<std::net::SocketAddr>;
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
use std::net::SocketAddr;
match self {
SocketAddress::TcpIpV4 { addr, port } => {
let ip_addr = std::net::Ipv4Addr::from(*addr);
let socket_addr = SocketAddr::new(ip_addr.into(), *port);
Ok(vec![socket_addr].into_iter())
}
SocketAddress::TcpIpV6 { addr, port } => {
let ip_addr = std::net::Ipv6Addr::from(*addr);
let socket_addr = SocketAddr::new(ip_addr.into(), *port);
Ok(vec![socket_addr].into_iter())
}
SocketAddress::Hostname { hostname, port } => {
(hostname.as_str(), *port).to_socket_addrs()
}
SocketAddress::OnionV2(..) => Err(std::io::Error::other(
"Resolution of OnionV2 addresses is currently unsupported.",
)),
SocketAddress::OnionV3 { .. } => Err(std::io::Error::other(
"Resolution of OnionV3 addresses is currently unsupported.",
)),
}
}
}
impl Display for SocketAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
SocketAddress::TcpIpV4 { addr, port } => write!(
f,
"{}.{}.{}.{}:{}",
addr[0], addr[1], addr[2], addr[3], port
)?,
SocketAddress::TcpIpV6 { addr, port } => write!(
f,
"[{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}]:{}",
addr[0],
addr[1],
addr[2],
addr[3],
addr[4],
addr[5],
addr[6],
addr[7],
addr[8],
addr[9],
addr[10],
addr[11],
addr[12],
addr[13],
addr[14],
addr[15],
port
)?,
SocketAddress::OnionV2(bytes) => write!(f, "OnionV2({:?})", bytes)?,
SocketAddress::OnionV3 {
ed25519_pubkey,
checksum,
version,
port,
} => {
let [first_checksum_flag, second_checksum_flag] = checksum.to_be_bytes();
let mut addr = vec![*version, first_checksum_flag, second_checksum_flag];
addr.extend_from_slice(ed25519_pubkey);
let onion = base32::Alphabet::RFC4648 { padding: false }.encode(&addr);
write!(f, "{}.onion:{}", onion, port)?
}
SocketAddress::Hostname { hostname, port } => write!(f, "{}:{}", hostname, port)?,
}
Ok(())
}
}
impl FromStr for SocketAddress {
type Err = SocketAddressParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match std::net::SocketAddr::from_str(s) {
Ok(addr) => Ok(addr.into()),
Err(_) => {
let trimmed_input = match s.rfind(":") {
Some(pos) => pos,
None => return Err(SocketAddressParseError::InvalidInput),
};
let host = &s[..trimmed_input];
let port: u16 = s[trimmed_input + 1..]
.parse()
.map_err(|_| SocketAddressParseError::InvalidPort)?;
if host.ends_with(".onion") {
return parse_onion_address(host, port);
};
if let Ok(hostname) = Hostname::try_from(s[..trimmed_input].to_string()) {
return Ok(SocketAddress::Hostname { hostname, port });
};
Err(SocketAddressParseError::SocketAddrParse)
}
}
}
}
pub fn parse_onion_address(
host: &str,
port: u16,
) -> Result<SocketAddress, SocketAddressParseError> {
if host.ends_with(".onion") {
let domain = if let Some(domain) = host.strip_suffix(".onion") {
if domain.len() != 56 {
return Err(SocketAddressParseError::InvalidOnionV3);
}
domain
} else {
return Err(SocketAddressParseError::InvalidOnionV3);
};
let onion = base32::Alphabet::RFC4648 { padding: false }
.decode(domain)
.map_err(|_| SocketAddressParseError::InvalidOnionV3)?;
if onion.len() != 35 {
return Err(SocketAddressParseError::InvalidOnionV3);
}
let version = onion[0];
let first_checksum_flag = onion[1];
let second_checksum_flag = onion[2];
let mut ed25519_pubkey = [0; 32];
ed25519_pubkey.copy_from_slice(&onion[3..35]);
let checksum = u16::from_be_bytes([first_checksum_flag, second_checksum_flag]);
Ok(SocketAddress::OnionV3 {
ed25519_pubkey,
checksum,
version,
port,
})
} else {
Err(SocketAddressParseError::InvalidInput)
}
}