pub mod buffer;
pub mod default;
pub mod i2p;
pub use buffer::Buffer;
pub use default::Default;
pub use i2p::I2p;
use anyhow::{Result, bail};
use cyphernet::addr::{HostName, i2p::I2pAddr};
use std::{
net::{IpAddr, Ipv4Addr, Ipv6Addr},
str::FromStr,
};
pub const IP4_LEN: usize = 6;
pub const IP6_LEN: usize = 18;
pub const I2P_LEN: usize = 32;
#[derive(Debug)]
pub enum Peer {
Default(Default),
I2p(I2p),
}
impl Peer {
pub fn from_be_bytes(buffer: &[u8]) -> Result<Self> {
match buffer.len() {
IP4_LEN => Self::from_ip4_be_bytes(buffer.try_into()?),
IP6_LEN => Self::from_ip6_be_bytes(buffer.try_into()?),
I2P_LEN => Self::from_i2p_be_bytes(buffer.try_into()?),
l => bail!("unexpected bytes len {l}"),
}
}
pub fn from_ip4_be_bytes(buffer: &[u8; IP4_LEN]) -> Result<Self> {
const H: usize = 4;
Ok(Peer::Default(Default {
host: IpAddr::V4(Ipv4Addr::from({
let ip_bytes: [u8; H] = buffer[0..H].try_into()?;
ip_bytes
})),
port: u16::from_be_bytes(buffer[H..IP4_LEN].try_into()?),
}))
}
pub fn from_ip6_be_bytes(buffer: &[u8; IP6_LEN]) -> Result<Self> {
const H: usize = 16;
Ok(Peer::Default(Default {
host: IpAddr::V6(Ipv6Addr::from({
let ip_bytes: [u8; H] = buffer[0..H].try_into()?;
ip_bytes
})),
port: u16::from_be_bytes(buffer[H..IP6_LEN].try_into()?),
}))
}
pub fn from_i2p_be_bytes(buffer: &[u8; I2P_LEN]) -> Result<Self> {
Ok(Peer::I2p(I2p {
b32: HostName::I2p(I2pAddr::from_str(&format!(
"{}.b32.i2p",
data_encoding::BASE32_NOPAD.encode(buffer)
))?)
.to_string(),
}))
}
}
impl std::fmt::Display for Peer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Default(this) => write!(f, "{this}"),
Self::I2p(this) => write!(f, "{this}"),
}
}
}