use crate::decoding::traits::DecodeError;
use crate::prelude::*;
#[cfg(feature = "std")]
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use super::vsf_type::VsfType;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NaScheme {
Https = 0,
Http = 1,
Ftp = 2,
Sftp = 3,
Ssh = 4,
Ntp = 5,
Smtp = 6,
Ws = 7,
Wss = 8,
}
impl NaScheme {
pub fn from_byte(b: u8) -> Option<Self> {
Some(match b {
0 => Self::Https,
1 => Self::Http,
2 => Self::Ftp,
3 => Self::Sftp,
4 => Self::Ssh,
5 => Self::Ntp,
6 => Self::Smtp,
7 => Self::Ws,
8 => Self::Wss,
_ => return None,
})
}
pub const fn as_byte(self) -> u8 {
self as u8
}
pub const fn as_str(self) -> &'static str {
match self {
Self::Https => "https",
Self::Http => "http",
Self::Ftp => "ftp",
Self::Sftp => "sftp",
Self::Ssh => "ssh",
Self::Ntp => "ntp",
Self::Smtp => "smtp",
Self::Ws => "ws",
Self::Wss => "wss",
}
}
pub const fn default_port(self) -> Option<u16> {
Some(match self {
Self::Https => 443,
Self::Http => 80,
Self::Ftp => 21,
Self::Sftp => 22,
Self::Ssh => 22,
Self::Ntp => 123,
Self::Smtp => 25,
Self::Ws => 80,
Self::Wss => 443,
})
}
}
impl core::fmt::Display for NaScheme {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct WaAddress {
pub line1: Option<String>, pub line2: Option<String>, pub city: Option<String>,
pub region: Option<String>, pub postal_code: Option<String>, pub country: Option<[u8; 2]>, }
impl WaAddress {
pub fn new() -> Self {
Self::default()
}
pub fn with_line1(mut self, v: impl Into<String>) -> Self {
self.line1 = Some(v.into());
self
}
pub fn with_line2(mut self, v: impl Into<String>) -> Self {
self.line2 = Some(v.into());
self
}
pub fn with_city(mut self, v: impl Into<String>) -> Self {
self.city = Some(v.into());
self
}
pub fn with_region(mut self, v: impl Into<String>) -> Self {
self.region = Some(v.into());
self
}
pub fn with_postal_code(mut self, v: impl Into<String>) -> Self {
self.postal_code = Some(v.into());
self
}
pub fn with_country(mut self, v: [u8; 2]) -> Self {
self.country = Some(v);
self
}
pub fn to_wire(&self) -> Vec<u8> {
let mut mask: u8 = 0;
if self.line1.is_some() {
mask |= 0x01;
}
if self.line2.is_some() {
mask |= 0x02;
}
if self.city.is_some() {
mask |= 0x04;
}
if self.region.is_some() {
mask |= 0x08;
}
if self.postal_code.is_some() {
mask |= 0x10;
}
if self.country.is_some() {
mask |= 0x20;
}
let mut out = vec![mask];
let push_str = |out: &mut Vec<u8>, s: &str| {
out.extend_from_slice(s.as_bytes());
out.push(0);
};
if let Some(s) = &self.line1 {
push_str(&mut out, s);
}
if let Some(s) = &self.line2 {
push_str(&mut out, s);
}
if let Some(s) = &self.city {
push_str(&mut out, s);
}
if let Some(s) = &self.region {
push_str(&mut out, s);
}
if let Some(s) = &self.postal_code {
push_str(&mut out, s);
}
if let Some(c) = &self.country {
out.extend_from_slice(c);
}
out
}
pub fn from_wire(data: &[u8], pointer: &mut usize) -> Result<Self, DecodeError> {
if *pointer >= data.len() {
return Err(DecodeError::UnexpectedEofMsg("wa: missing mask".into()));
}
let mask = data[*pointer];
*pointer += 1;
let read_str = |data: &[u8], pointer: &mut usize| -> Result<String, DecodeError> {
let start = *pointer;
while *pointer < data.len() && data[*pointer] != 0 {
*pointer += 1;
}
if *pointer >= data.len() {
return Err(DecodeError::UnexpectedEofMsg(
"wa: unterminated string".into(),
));
}
let s = core::str::from_utf8(&data[start..*pointer])
.map_err(|e| DecodeError::InvalidDataMsg(format!("{}", e)))?
.to_string();
*pointer += 1; Ok(s)
};
let mut addr = WaAddress::default();
if mask & 0x01 != 0 {
addr.line1 = Some(read_str(data, pointer)?);
}
if mask & 0x02 != 0 {
addr.line2 = Some(read_str(data, pointer)?);
}
if mask & 0x04 != 0 {
addr.city = Some(read_str(data, pointer)?);
}
if mask & 0x08 != 0 {
addr.region = Some(read_str(data, pointer)?);
}
if mask & 0x10 != 0 {
addr.postal_code = Some(read_str(data, pointer)?);
}
if mask & 0x20 != 0 {
if *pointer + 2 > data.len() {
return Err(DecodeError::UnexpectedEofMsg(
"wa: country truncated".into(),
));
}
addr.country = Some([data[*pointer], data[*pointer + 1]]);
*pointer += 2;
}
Ok(addr)
}
pub fn wire_len(&self) -> usize {
let str_len = |s: &Option<String>| s.as_ref().map_or(0, |v| v.len() + 1);
1 + str_len(&self.line1)
+ str_len(&self.line2)
+ str_len(&self.city)
+ str_len(&self.region)
+ str_len(&self.postal_code)
+ self.country.map_or(0, |_| 2)
}
}
impl core::fmt::Display for WaAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut parts = Vec::new();
if let Some(s) = &self.line1 {
parts.push(s.as_str().to_string());
}
if let Some(s) = &self.line2 {
parts.push(s.as_str().to_string());
}
if let Some(s) = &self.city {
parts.push(s.as_str().to_string());
}
if let Some(s) = &self.region {
parts.push(s.as_str().to_string());
}
if let Some(s) = &self.postal_code {
parts.push(s.as_str().to_string());
}
if let Some(c) = &self.country {
if let Ok(s) = core::str::from_utf8(c) {
parts.push(s.to_string());
}
}
write!(f, "{}", parts.join(", "))
}
}
#[cfg(feature = "std")]
impl From<Ipv4Addr> for VsfType {
fn from(a: Ipv4Addr) -> Self {
VsfType::ni(a.octets())
}
}
#[cfg(feature = "std")]
impl From<Ipv6Addr> for VsfType {
fn from(a: Ipv6Addr) -> Self {
VsfType::nj(a.octets())
}
}
#[cfg(feature = "std")]
impl From<IpAddr> for VsfType {
fn from(a: IpAddr) -> Self {
match a {
IpAddr::V4(v4) => VsfType::ni(v4.octets()),
IpAddr::V6(v6) => VsfType::nj(v6.octets()),
}
}
}
#[cfg(feature = "std")]
impl From<SocketAddrV4> for VsfType {
fn from(a: SocketAddrV4) -> Self {
VsfType::ns(a.ip().to_string(), a.port())
}
}
#[cfg(feature = "std")]
impl From<SocketAddrV6> for VsfType {
fn from(a: SocketAddrV6) -> Self {
VsfType::ns(format!("[{}]", a.ip()), a.port())
}
}
#[cfg(feature = "std")]
impl From<SocketAddr> for VsfType {
fn from(a: SocketAddr) -> Self {
match a {
SocketAddr::V4(v4) => VsfType::from(v4),
SocketAddr::V6(v6) => VsfType::from(v6),
}
}
}
impl From<WaAddress> for VsfType {
fn from(a: WaAddress) -> Self {
VsfType::wa(a)
}
}
#[cfg(feature = "std")]
impl TryFrom<VsfType> for Ipv4Addr {
type Error = &'static str;
fn try_from(v: VsfType) -> Result<Self, Self::Error> {
match v {
VsfType::ni(b) => Ok(Ipv4Addr::from(b)),
VsfType::nh(s) => s.parse().map_err(|_| "not a valid IPv4 string"),
_ => Err("not an IPv4 type"),
}
}
}
#[cfg(feature = "std")]
impl TryFrom<VsfType> for Ipv6Addr {
type Error = &'static str;
fn try_from(v: VsfType) -> Result<Self, Self::Error> {
match v {
VsfType::nj(b) => Ok(Ipv6Addr::from(b)),
VsfType::nh(s) => s
.trim_matches(|c| c == '[' || c == ']')
.parse()
.map_err(|_| "not a valid IPv6 string"),
_ => Err("not an IPv6 type"),
}
}
}
#[cfg(feature = "std")]
impl TryFrom<VsfType> for IpAddr {
type Error = &'static str;
fn try_from(v: VsfType) -> Result<Self, Self::Error> {
match v {
VsfType::ni(b) => Ok(IpAddr::V4(Ipv4Addr::from(b))),
VsfType::nj(b) => Ok(IpAddr::V6(Ipv6Addr::from(b))),
VsfType::nh(s) => s.parse().map_err(|_| "not a valid IP address string"),
_ => Err("not an IP address type"),
}
}
}
#[cfg(feature = "std")]
impl TryFrom<VsfType> for SocketAddr {
type Error = &'static str;
fn try_from(v: VsfType) -> Result<Self, Self::Error> {
match v {
VsfType::ns(host, port) => {
let addr_str = format!("{}:{}", host, port);
addr_str.parse().map_err(|_| "not a valid socket address")
}
_ => Err("not a socket address type"),
}
}
}