koibumi-core 0.0.9

The core library for Koibumi, an experimental Bitmessage client
Documentation
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,
};

/// An extended network address object
/// that can represent IPv4, IPv6, Onion v2 or Onion v3 address.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Onionpeer {
    port: VarInt,
    addr: Vec<u8>,
}

/// The error type returned when a conversion from a onionpeer object to an extended socket address fails.
///
/// This error is used as the error type for the `TryFrom` implementation
/// for `SocketAddrExt`.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum TryFromOnionpeerError {
    /// Indicates that the port number is out of the range.
    /// The actual number is returned as a payload of this variant.
    InvalidPort(u64),
    /// Indicates that the length of the address is invalid.
    /// The actual length is returned as a payload of this variant.
    InvalidLength(usize),
    /// Indicates that the prefix bytes of the Onion address is invalid.
    /// The actual prefix is returned as a payload of this variant.
    InvalidPrefix([u8; 6]),
    /// An error was caught during parsing an Onion V3 address.
    /// The actual error caught is returned as a payload of this variant.
    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 })
    }
}