use std::{
convert::{TryFrom, TryInto},
fmt,
io::{self, Read, Write},
};
use koibumi_net::Port;
use crate::{
io::{LenBm, ReadFrom, SizedReadFrom, WriteTo},
net::{Addr, AddrExt, OnionV3Addr, ParseOnionV3AddrError, SocketAddrExt},
priv_util::ToHexString,
var_type::VarInt,
};
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Onionpeer {
port: VarInt,
addr: Vec<u8>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum TryFromOnionpeerError {
InvalidPort(u64),
InvalidLength(usize),
InvalidPrefix([u8; 6]),
ParseOnionV3AddrError(ParseOnionV3AddrError),
}
impl fmt::Display for TryFromOnionpeerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidPort(port) => write!(f, "invalid port: {}", port),
Self::InvalidLength(len) => write!(f, "invalid length: {}", len),
Self::InvalidPrefix(prefix) => {
write!(f, "invalid prefix: {}", prefix.as_ref().to_hex_string())
}
Self::ParseOnionV3AddrError(err) => err.fmt(f),
}
}
}
impl std::error::Error for TryFromOnionpeerError {}
impl From<ParseOnionV3AddrError> for TryFromOnionpeerError {
fn from(err: ParseOnionV3AddrError) -> Self {
Self::ParseOnionV3AddrError(err)
}
}
const ONION_PREFIX: [u8; 6] = [0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43];
impl TryFrom<Onionpeer> for SocketAddrExt {
type Error = TryFromOnionpeerError;
fn try_from(op: Onionpeer) -> Result<Self, <Self as TryFrom<Onionpeer>>::Error> {
if op.port.as_u64() > u16::MAX as u64 {
return Err(TryFromOnionpeerError::InvalidPort(op.port.as_u64()));
}
let port = Port::new(op.port.as_u64() as u16);
let len = op.addr.len();
if len == 16 {
let bytes: [u8; 16] = op.addr[..].try_into().unwrap();
let addr: Addr = bytes.into();
let addr: AddrExt = addr.into();
Ok(Self::new(addr, port))
} else if len == 6 + 35 {
if op.addr[0..6] != *ONION_PREFIX.as_ref() {
let prefix = op.addr[0..6].try_into().unwrap();
return Err(TryFromOnionpeerError::InvalidPrefix(prefix));
}
let mut bytes = [0; 35];
bytes.copy_from_slice(&op.addr[6..]);
let addr = OnionV3Addr::new(bytes)?;
let addr = AddrExt::OnionV3(addr);
Ok(Self::new(addr, port))
} else {
Err(TryFromOnionpeerError::InvalidLength(len))
}
}
}
const IPV4_PREFIX: [u8; 12] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff];
impl From<SocketAddrExt> for Onionpeer {
fn from(addr: SocketAddrExt) -> Self {
match addr {
SocketAddrExt::Ipv4(sa) => {
let port: VarInt = (sa.port() as u64).into();
let mut addr = IPV4_PREFIX.clone().to_vec();
addr.extend_from_slice(&sa.ip().octets());
Self { port, addr }
}
SocketAddrExt::Ipv6(sa) => {
let port: VarInt = (sa.port() as u64).into();
let addr = sa.ip().octets().to_vec();
Self { port, addr }
}
SocketAddrExt::OnionV2(sa) => {
let port: VarInt = (sa.port().as_u16() as u64).into();
let mut addr = ONION_PREFIX.clone().to_vec();
addr.extend_from_slice(sa.onion_addr().as_ref());
Self { port, addr }
}
SocketAddrExt::OnionV3(sa) => {
let port: VarInt = (sa.port().as_u16() as u64).into();
let mut addr = ONION_PREFIX.clone().to_vec();
addr.extend_from_slice(sa.onion_addr().as_ref());
Self { port, addr }
}
}
}
}
impl WriteTo for Onionpeer {
fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
self.port.write_to(w)?;
self.addr.write_to(w)?;
Ok(())
}
}
impl SizedReadFrom for Onionpeer {
fn sized_read_from(r: &mut dyn Read, len: usize) -> io::Result<Self>
where
Self: Sized,
{
let port = VarInt::read_from(r)?;
let addr = Vec::<u8>::sized_read_from(r, len - port.len_bm())?;
Ok(Self { port, addr })
}
}