use core::fmt::{Binary, Debug, Display};
use core::hash::Hash;
use core::mem;
use core::ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Not, RangeInclusive, Shl, Shr, Sub};
use bitvec::{slice::BitSlice, BitArr};
use num_traits::CheckedAdd;
use super::Afi;
use crate::{
concrete::{Ipv4, Ipv6},
error::Error,
parser,
};
pub trait Address<A: Afi>:
Copy
+ Debug
+ Default
+ Hash
+ Ord
+ BitAnd<Self, Output = Self>
+ BitOr<Self, Output = Self>
+ BitXor<Self, Output = Self>
+ Add<Self, Output = Self>
+ CheckedAdd
+ Not<Output = Self>
+ Shl<Self::Length, Output = Self>
+ Shr<Self::Length, Output = Self>
+ 'static
{
type Length: Length;
type LengthMap: LengthMap;
const MIN_LENGTH: Self::Length = Self::Length::ZERO;
const MAX_LENGTH: Self::Length;
const ZERO: Self;
const ONES: Self;
const BROADCAST: Option<Self>;
const LOCALHOST: Self;
const UNSPECIFIED: Self;
const LOOPBACK_RANGE: RangeInclusive<Self>;
const BENCHMARK_RANGE: RangeInclusive<Self>;
const MULTICAST_RANGE: RangeInclusive<Self>;
const LINK_LOCAL_RANGE: RangeInclusive<Self>;
const PROTOCOL_ASSIGNMENTS_RANGE: RangeInclusive<Self>;
const DOCUMENTATION_RANGES: &'static [RangeInclusive<Self>];
const PRIVATE_RANGES: Option<&'static [RangeInclusive<Self>]>;
const RESERVED_RANGE: Option<RangeInclusive<Self>>;
const SHARED_RANGE: Option<RangeInclusive<Self>>;
const THISNET_RANGE: Option<RangeInclusive<Self>>;
const ULA_RANGE: Option<RangeInclusive<Self>>;
fn leading_zeros(self) -> Self::Length;
fn to_be_bytes(self) -> A::Octets;
fn from_be_bytes(bytes: A::Octets) -> Self;
fn is_global(&self) -> bool;
fn parse_addr<S>(s: &S) -> Result<Self, Error>
where
S: AsRef<str> + ?Sized;
fn parse_prefix<S>(s: &S) -> Result<(Self, Self::Length), Error>
where
S: AsRef<str> + ?Sized;
#[allow(clippy::type_complexity)]
fn parse_range<S>(s: &S) -> Result<(Self, Self::Length, Self::Length, Self::Length), Error>
where
S: AsRef<str> + ?Sized;
}
macro_rules! ipv4 {
($a:literal, $b:literal, $c:literal, $d:literal) => {
u32::from_be_bytes([$a, $b, $c, $d])
};
}
impl Address<Ipv4> for u32 {
type Length = u8;
type LengthMap = BitArr!(for 33);
const MAX_LENGTH: Self::Length = 32;
const ZERO: Self = ipv4!(0, 0, 0, 0);
const ONES: Self = ipv4!(255, 255, 255, 255);
const BROADCAST: Option<Self> = Some(ipv4!(255, 255, 255, 255));
const LOCALHOST: Self = ipv4!(127, 0, 0, 1);
const UNSPECIFIED: Self = ipv4!(0, 0, 0, 0);
const LOOPBACK_RANGE: RangeInclusive<Self> = ipv4!(127, 0, 0, 0)..=ipv4!(127, 255, 255, 255);
const BENCHMARK_RANGE: RangeInclusive<Self> = ipv4!(198, 18, 0, 0)..=ipv4!(198, 19, 255, 255);
const MULTICAST_RANGE: RangeInclusive<Self> = ipv4!(224, 0, 0, 0)..=ipv4!(239, 255, 255, 255);
const LINK_LOCAL_RANGE: RangeInclusive<Self> =
ipv4!(169, 254, 0, 0)..=ipv4!(169, 254, 255, 255);
const PROTOCOL_ASSIGNMENTS_RANGE: RangeInclusive<Self> =
ipv4!(192, 0, 0, 0)..=ipv4!(192, 0, 0, 255);
const DOCUMENTATION_RANGES: &'static [RangeInclusive<Self>] = &[
ipv4!(192, 0, 2, 0)..=ipv4!(192, 0, 2, 255),
ipv4!(198, 51, 100, 0)..=ipv4!(198, 51, 100, 255),
ipv4!(203, 0, 113, 0)..=ipv4!(203, 0, 113, 255),
];
const PRIVATE_RANGES: Option<&'static [RangeInclusive<Self>]> = Some(&[
ipv4!(10, 0, 0, 0)..=ipv4!(10, 255, 255, 255),
ipv4!(172, 16, 0, 0)..=ipv4!(172, 31, 255, 255),
ipv4!(192, 168, 0, 0)..=ipv4!(192, 168, 255, 255),
]);
const RESERVED_RANGE: Option<RangeInclusive<Self>> =
Some(ipv4!(240, 0, 0, 0)..=ipv4!(255, 255, 255, 255));
const SHARED_RANGE: Option<RangeInclusive<Self>> =
Some(ipv4!(100, 64, 0, 0)..=ipv4!(100, 127, 255, 255));
const THISNET_RANGE: Option<RangeInclusive<Self>> =
Some(ipv4!(0, 0, 0, 0)..=ipv4!(0, 255, 255, 255));
const ULA_RANGE: Option<RangeInclusive<Self>> = None;
#[allow(clippy::cast_possible_truncation)]
fn leading_zeros(self) -> Self::Length {
self.leading_zeros() as Self::Length
}
fn to_be_bytes(self) -> <Ipv4 as Afi>::Octets {
self.to_be_bytes()
}
#[allow(clippy::only_used_in_recursion)]
fn from_be_bytes(bytes: <Ipv4 as Afi>::Octets) -> Self {
Self::from_be_bytes(bytes)
}
fn is_global(&self) -> bool {
if Self::LOOPBACK_RANGE.contains(self)
|| Self::LINK_LOCAL_RANGE.contains(self)
|| Self::BENCHMARK_RANGE.contains(self)
|| Self::DOCUMENTATION_RANGES
.iter()
.any(|range| range.contains(self))
{
return false;
}
if let Some(ref broadcast) = Self::BROADCAST {
if broadcast == self {
return false;
}
}
if let Some(ranges) = Self::PRIVATE_RANGES {
if ranges.iter().any(|range| range.contains(self)) {
return false;
}
}
if let Some(range) = Self::SHARED_RANGE {
if range.contains(self) {
return false;
}
}
if let Some(range) = Self::RESERVED_RANGE {
if range.contains(self) {
return false;
}
}
if let Some(range) = Self::THISNET_RANGE {
if range.contains(self) {
return false;
}
}
if Self::MULTICAST_RANGE.contains(self)
&& !(ipv4!(224, 0, 1, 0)..=ipv4!(238, 255, 255, 255)).contains(self)
{
return false;
}
if Self::PROTOCOL_ASSIGNMENTS_RANGE.contains(self)
&& self != &ipv4!(192, 0, 0, 9)
&& self != &ipv4!(192, 0, 0, 10)
{
return false;
}
true
}
fn parse_addr<S>(s: &S) -> Result<Self, Error>
where
S: AsRef<str> + ?Sized,
{
parser::ipv4::parse_addr(s.as_ref())
}
fn parse_prefix<S>(s: &S) -> Result<(Self, Self::Length), Error>
where
S: AsRef<str> + ?Sized,
{
parser::ipv4::parse_prefix(s.as_ref())
}
fn parse_range<S>(s: &S) -> Result<(Self, Self::Length, Self::Length, Self::Length), Error>
where
S: AsRef<str> + ?Sized,
{
parser::ipv4::parse_range(s.as_ref())
}
}
impl Address<Ipv6> for u128 {
type Length = u8;
type LengthMap = BitArr!(for 129);
const MAX_LENGTH: Self::Length = 128;
const ZERO: Self = 0x0000_0000_0000_0000_0000_0000_0000_0000;
const ONES: Self = 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff;
const BROADCAST: Option<Self> = None;
const LOCALHOST: Self = 0x0000_0000_0000_0000_0000_0000_0000_0001;
const UNSPECIFIED: Self = Self::ZERO;
const LOOPBACK_RANGE: RangeInclusive<Self> = 0x1..=0x1;
const BENCHMARK_RANGE: RangeInclusive<Self> =
0x2001_0002_0000_0000_0000_0000_0000_0000..=0x2001_0002_0000_ffff_ffff_ffff_ffff_ffff;
const MULTICAST_RANGE: RangeInclusive<Self> =
0xff00_0000_0000_0000_0000_0000_0000_0000..=0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff;
const LINK_LOCAL_RANGE: RangeInclusive<Self> =
0xfe80_0000_0000_0000_0000_0000_0000_0000..=0xfebf_ffff_ffff_ffff_ffff_ffff_ffff_ffff;
const PROTOCOL_ASSIGNMENTS_RANGE: RangeInclusive<Self> =
0x2001_0000_0000_0000_0000_0000_0000_0000..=0x2001_01ff_ffff_ffff_ffff_ffff_ffff_ffff;
const DOCUMENTATION_RANGES: &'static [RangeInclusive<Self>] =
&[(0x2001_0db8_0000_0000_0000_0000_0000_0000..=0x2001_0db8_ffff_ffff_ffff_ffff_ffff_ffff)];
const PRIVATE_RANGES: Option<&'static [RangeInclusive<Self>]> = None;
const RESERVED_RANGE: Option<RangeInclusive<Self>> = None;
const SHARED_RANGE: Option<RangeInclusive<Self>> = None;
const THISNET_RANGE: Option<RangeInclusive<Self>> = None;
const ULA_RANGE: Option<RangeInclusive<Self>> =
Some(0xfc00_0000_0000_0000_0000_0000_0000_0000..=0xfd00_0000_0000_0000_0000_0000_0000_0000);
#[allow(clippy::cast_possible_truncation)]
fn leading_zeros(self) -> Self::Length {
self.leading_zeros() as Self::Length
}
fn to_be_bytes(self) -> <Ipv6 as Afi>::Octets {
self.to_be_bytes()
}
fn from_be_bytes(bytes: <Ipv6 as Afi>::Octets) -> Self {
Self::from_be_bytes(bytes)
}
fn is_global(&self) -> bool {
if Self::LOOPBACK_RANGE.contains(self)
|| Self::LINK_LOCAL_RANGE.contains(self)
|| self == &Self::UNSPECIFIED
|| Self::BENCHMARK_RANGE.contains(self)
|| Self::DOCUMENTATION_RANGES
.iter()
.any(|range| range.contains(self))
{
return false;
}
if let Some(range) = Self::ULA_RANGE {
if range.contains(self) {
return false;
}
}
if Self::MULTICAST_RANGE.contains(self)
&& self & 0x000f_0000_0000_0000_0000_0000_0000_0000
!= 0x000e_0000_0000_0000_0000_0000_0000_0000
{
return false;
}
if Self::PROTOCOL_ASSIGNMENTS_RANGE.contains(self)
&& !(0x2001_0000_0000_0000_0000_0000_0000_0000..=0x2001_0000_ffff_ffff_ffff_ffff_ffff_ffff).contains(self)
&& self != &0x2001_0001_0000_0000_0000_0000_0000_0001
&& self != &0x2001_0001_0000_0000_0000_0000_0000_0002
&& !(0x2001_0003_0000_0000_0000_0000_0000_0000..=0x2001_0003_ffff_ffff_ffff_ffff_ffff_ffff).contains(self)
&& !(0x2001_0004_0112_0000_0000_0000_0000_0000..=0x2001_0004_0112_ffff_ffff_ffff_ffff_ffff).contains(self)
&& !(0x2001_0020_0000_0000_0000_0000_0000_0000..=0x2001_002f_ffff_ffff_ffff_ffff_ffff_ffff).contains(self)
{
return false;
}
true
}
fn parse_addr<S>(s: &S) -> Result<Self, Error>
where
S: AsRef<str> + ?Sized,
{
parser::ipv6::parse_addr(s.as_ref())
}
fn parse_prefix<S>(s: &S) -> Result<(Self, Self::Length), Error>
where
S: AsRef<str> + ?Sized,
{
parser::ipv6::parse_prefix(s.as_ref())
}
fn parse_range<S>(s: &S) -> Result<(Self, Self::Length, Self::Length, Self::Length), Error>
where
S: AsRef<str> + ?Sized,
{
parser::ipv6::parse_range(s.as_ref())
}
}
pub(crate) trait IntoIpv6Segments: Address<Ipv6> {
#[allow(unsafe_code)]
fn into_segments(self) -> [u16; 8] {
let [a, b, c, d, e, f, g, h]: [u16; 8] = unsafe { mem::transmute(self.to_be_bytes()) };
[
u16::from_be(a),
u16::from_be(b),
u16::from_be(c),
u16::from_be(d),
u16::from_be(e),
u16::from_be(f),
u16::from_be(g),
u16::from_be(h),
]
}
#[allow(unsafe_code)]
#[allow(clippy::inline_always)]
#[inline(always)]
fn from_segments(segments: [u16; 8]) -> Self {
let octets = unsafe {
core::mem::transmute([
segments[0].to_be(),
segments[1].to_be(),
segments[2].to_be(),
segments[3].to_be(),
segments[4].to_be(),
segments[5].to_be(),
segments[6].to_be(),
segments[7].to_be(),
])
};
Self::from_be_bytes(octets)
}
}
impl<P: Address<Ipv6>> IntoIpv6Segments for P {}
pub trait Length:
Copy
+ Clone
+ Debug
+ Display
+ Hash
+ Ord
+ Add<Output = Self>
+ Sub<Output = Self>
+ Into<usize>
+ TryFrom<usize>
{
const ZERO: Self;
const ONE: Self;
}
impl Length for u8 {
const ZERO: Self = 0;
const ONE: Self = 1;
}
pub trait LengthMap:
Copy
+ Binary
+ BitAnd<Output = Self>
+ BitOr<Output = Self>
+ Not<Output = Self>
+ Default
+ Deref<Target = BitSlice>
+ DerefMut<Target = BitSlice>
+ Eq
{
const ZERO: Self;
}
impl LengthMap for BitArr!(for 33) {
const ZERO: Self = Self::ZERO;
}
impl LengthMap for BitArr!(for 129) {
const ZERO: Self = Self::ZERO;
}