resolv_conf/
ip.rs

1use std::error::Error;
2use std::fmt;
3use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4use std::str::FromStr;
5
6/// A network, that is an IP address and a mask
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub enum Network {
9    /// Represent an IPv4 network address
10    V4(Ipv4Addr, Ipv4Addr),
11    /// Represent an IPv6 network address
12    V6(Ipv6Addr, Ipv6Addr),
13}
14
15impl FromStr for Network {
16    type Err = AddrParseError;
17
18    fn from_str(val: &str) -> Result<Self, Self::Err> {
19        let (ip, mask) = match val.split_once('/') {
20            Some((ip, mask)) => (ip, Some(mask)),
21            None => (val, None),
22        };
23
24        match IpAddr::from_str(ip)? {
25            IpAddr::V4(ip) => {
26                if ip.is_unspecified() {
27                    return Err(AddrParseError);
28                }
29
30                let mask = match mask {
31                    Some(mask) => {
32                        let mask = Ipv4Addr::from_str(mask)?;
33                        // make sure this is a valid mask
34                        let value = ip.octets().iter().fold(0, |acc, &x| acc + u32::from(x));
35                        match value == 0 || (value & !value != 0) {
36                            true => return Err(AddrParseError),
37                            false => mask,
38                        }
39                    }
40                    // We have to "guess" the mask.
41                    //
42                    // FIXME(@little-dude) right now, we look at the number or bytes that are 0,
43                    // but maybe we should use the number of bits that are 0.
44                    //
45                    // In other words, with this implementation, the mask of `128.192.0.0` will be
46                    // `255.255.0.0` (a.k.a `/16`). But we could also consider that the mask is
47                    // `/10` (a.k.a `255.63.0.0`).
48                    //
49                    // My only source on topic is the "DNS and Bind" book which suggests using
50                    // bytes, not bits.
51                    None => {
52                        let octets = ip.octets();
53                        if octets[3] == 0 {
54                            if octets[2] == 0 {
55                                if octets[1] == 0 {
56                                    Ipv4Addr::new(255, 0, 0, 0)
57                                } else {
58                                    Ipv4Addr::new(255, 255, 0, 0)
59                                }
60                            } else {
61                                Ipv4Addr::new(255, 255, 255, 0)
62                            }
63                        } else {
64                            Ipv4Addr::new(255, 255, 255, 255)
65                        }
66                    }
67                };
68
69                Ok(Self::V4(ip, mask))
70            }
71            IpAddr::V6(ip) => {
72                let mask = match mask {
73                    // FIXME: validate the mask
74                    Some(mask) => Ipv6Addr::from_str(mask)?,
75                    // FIXME: "guess" an appropriate mask for the IP
76                    None => Ipv6Addr::new(
77                        65_535, 65_535, 65_535, 65_535, 65_535, 65_535, 65_535, 65_535,
78                    ),
79                };
80
81                Ok(Self::V6(ip, mask))
82            }
83        }
84    }
85}
86
87impl fmt::Display for Network {
88    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
89        match *self {
90            Self::V4(address, mask) => write!(fmt, "{address}/{mask}"),
91            Self::V6(address, mask) => write!(fmt, "{address}/{mask}"),
92        }
93    }
94}
95
96/// Represent an IP address. This type is similar to `std::net::IpAddr` but it supports IPv6 scope
97/// identifiers.
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum ScopedIp {
100    /// Represent an IPv4 address
101    V4(Ipv4Addr),
102    /// Represent an IPv6 and its scope identifier, if any
103    V6(Ipv6Addr, Option<String>),
104}
105
106impl From<ScopedIp> for IpAddr {
107    fn from(val: ScopedIp) -> Self {
108        match val {
109            ScopedIp::V4(ip) => Self::from(ip),
110            ScopedIp::V6(ip, _) => Self::from(ip),
111        }
112    }
113}
114
115impl From<&ScopedIp> for IpAddr {
116    fn from(val: &ScopedIp) -> Self {
117        match val {
118            ScopedIp::V4(ip) => Self::from(*ip),
119            ScopedIp::V6(ip, _) => Self::from(*ip),
120        }
121    }
122}
123
124impl From<Ipv6Addr> for ScopedIp {
125    fn from(value: Ipv6Addr) -> Self {
126        Self::V6(value, None)
127    }
128}
129
130impl From<Ipv4Addr> for ScopedIp {
131    fn from(value: Ipv4Addr) -> Self {
132        Self::V4(value)
133    }
134}
135
136impl From<IpAddr> for ScopedIp {
137    fn from(value: IpAddr) -> Self {
138        match value {
139            IpAddr::V4(ip) => Self::from(ip),
140            IpAddr::V6(ip) => Self::from(ip),
141        }
142    }
143}
144
145impl FromStr for ScopedIp {
146    type Err = AddrParseError;
147    /// Parse a string representing an IP address.
148    fn from_str(s: &str) -> Result<Self, AddrParseError> {
149        let mut parts = s.split('%');
150        let addr = parts.next().unwrap();
151        match IpAddr::from_str(addr) {
152            Ok(IpAddr::V4(ip)) => {
153                if parts.next().is_some() {
154                    // It's not a valid IPv4 address if it contains a '%'
155                    Err(AddrParseError)
156                } else {
157                    Ok(Self::from(ip))
158                }
159            }
160            Ok(IpAddr::V6(ip)) => {
161                if let Some(scope_id) = parts.next() {
162                    if scope_id.is_empty() {
163                        return Err(AddrParseError);
164                    }
165                    for c in scope_id.chars() {
166                        if !c.is_alphanumeric() {
167                            return Err(AddrParseError);
168                        }
169                    }
170                    Ok(Self::V6(ip, Some(scope_id.to_string())))
171                } else {
172                    Ok(Self::V6(ip, None))
173                }
174            }
175            Err(e) => Err(e.into()),
176        }
177    }
178}
179
180impl fmt::Display for ScopedIp {
181    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
182        match self {
183            Self::V4(address) => address.fmt(fmt),
184            Self::V6(address, None) => address.fmt(fmt),
185            Self::V6(address, Some(scope)) => write!(fmt, "{address}%{scope}"),
186        }
187    }
188}
189
190/// An error which can be returned when parsing an IP address.
191#[derive(Debug, Clone, PartialEq, Eq)]
192pub struct AddrParseError;
193
194impl fmt::Display for AddrParseError {
195    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
196        fmt.write_str("invalid IP address syntax")
197    }
198}
199
200impl Error for AddrParseError {}
201
202impl From<::std::net::AddrParseError> for AddrParseError {
203    fn from(_: ::std::net::AddrParseError) -> Self {
204        Self
205    }
206}