1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use std::error::Error;
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;

/// A network, that is an IP address and a mask
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Network {
    /// Represent an IPv4 network address
    V4(Ipv4Addr, Ipv4Addr),
    /// Represent an IPv6 network address
    V6(Ipv6Addr, Ipv6Addr),
}

impl fmt::Display for Network {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Network::V4(address, mask) => write!(fmt, "{}/{}", address, mask),
            Network::V6(address, mask) => write!(fmt, "{}/{}", address, mask),
        }
    }
}

/// Represent an IP address. This type is similar to `std::net::IpAddr` but it supports IPv6 scope
/// identifiers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ScopedIp {
    /// Represent an IPv4 address
    V4(Ipv4Addr),
    /// Represent an IPv6 and its scope identifier, if any
    V6(Ipv6Addr, Option<String>),
}

impl Into<IpAddr> for ScopedIp {
    fn into(self) -> IpAddr {
        match self {
            ScopedIp::V4(ip) => IpAddr::from(ip),
            ScopedIp::V6(ip, _) => IpAddr::from(ip),
        }
    }
}

impl<'a> Into<IpAddr> for &'a ScopedIp {
    fn into(self) -> IpAddr {
        match *self {
            ScopedIp::V4(ref ip) => IpAddr::from(*ip),
            ScopedIp::V6(ref ip, _) => IpAddr::from(*ip),
        }
    }
}

impl From<Ipv6Addr> for ScopedIp {
    fn from(value: Ipv6Addr) -> Self {
        ScopedIp::V6(value, None)
    }
}

impl From<Ipv4Addr> for ScopedIp {
    fn from(value: Ipv4Addr) -> Self {
        ScopedIp::V4(value)
    }
}

impl From<IpAddr> for ScopedIp {
    fn from(value: IpAddr) -> Self {
        match value {
            IpAddr::V4(ip) => ScopedIp::from(ip),
            IpAddr::V6(ip) => ScopedIp::from(ip),
        }
    }
}

impl FromStr for ScopedIp {
    type Err = AddrParseError;
    /// Parse a string representing an IP address.
    fn from_str(s: &str) -> Result<ScopedIp, AddrParseError> {
        let mut parts = s.split('%');
        let addr = parts.next().unwrap();
        match IpAddr::from_str(addr) {
            Ok(IpAddr::V4(ip)) => {
                if parts.next().is_some() {
                    // It's not a valid IPv4 address if it contains a '%'
                    Err(AddrParseError)
                } else {
                    Ok(ScopedIp::from(ip))
                }
            }
            Ok(IpAddr::V6(ip)) => if let Some(scope_id) = parts.next() {
                if scope_id.is_empty() {
                    return Err(AddrParseError);
                }
                for c in scope_id.chars() {
                    if !c.is_alphanumeric() {
                        return Err(AddrParseError);
                    }
                }
                Ok(ScopedIp::V6(ip, Some(scope_id.to_string())))
            } else {
                Ok(ScopedIp::V6(ip, None))
            },
            Err(e) => Err(e.into()),
        }
    }
}

impl fmt::Display for ScopedIp {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ScopedIp::V4(ref address) => address.fmt(fmt),
            ScopedIp::V6(ref address, None) => address.fmt(fmt),
            ScopedIp::V6(ref address, Some(ref scope)) => write!(fmt, "{}%{}", address, scope),
        }
    }
}

/// An error which can be returned when parsing an IP address.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddrParseError;

impl fmt::Display for AddrParseError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self.description())
    }
}

impl Error for AddrParseError {
    fn description(&self) -> &str {
        "invalid IP address syntax"
    }
}

impl From<::std::net::AddrParseError> for AddrParseError {
    fn from(_: ::std::net::AddrParseError) -> Self {
        AddrParseError
    }
}