postgres_inet/
lib.rs

1// Copyright 2017 Emma Welker et al.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Provides CIDR and Inet support for [`postgres`][1].
16//!
17//! Unlike several other names of this pattern, this is not affiliated
18//! with or supported by the [author][2] of [`postgres`][1].
19//!
20//! Please see the `examples/` folder in the crate root for a simple example.
21//!
22//! [1]: https://crates.io/crates/postgres
23//! [2]: https://github.com/sfackler
24#![doc(html_root_url = "https://docs.rs/postgres-inet/0.15.4")]
25#![forbid(
26    missing_copy_implementations,
27    missing_debug_implementations,
28    missing_docs,
29    trivial_casts,
30    trivial_numeric_casts,
31    unsafe_code,
32    unstable_features,
33    unused_extern_crates,
34    unused_import_braces
35)]
36
37extern crate bytes;
38
39#[cfg(feature = "ipnetwork")]
40extern crate ipnetwork;
41
42#[cfg(test)]
43extern crate postgres;
44
45#[macro_use]
46extern crate postgres_types;
47
48mod tests;
49
50use bytes::BytesMut;
51use postgres_types::{FromSql, IsNull, ToSql, Type};
52use std::error::Error;
53use std::fmt;
54use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr};
55use std::num::ParseIntError;
56use std::str::FromStr;
57
58const IPV4_CIDR_FULL: u8 = 32;
59const IPV4_ADDRESS_FAMILY: u8 = 2; // AF_INET (See Issue #1)
60const IPV4_ADDRESS_SIZE: u8 = 4;
61
62const IPV6_CIDR_FULL: u8 = 128;
63// Not AF_INET6; see postgres src/include/utils/inet.h
64const IPV6_ADDRESS_FAMILY: u8 = IPV4_ADDRESS_FAMILY + 1;
65const IPV6_ADDRESS_SIZE: u8 = 16;
66
67#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
68/// An IP address, if necessary in [CIDR notation].
69///
70/// [CIDR notation]: https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation
71pub struct MaskedIpAddr {
72    addr: IpAddr,
73    cidr: u8,
74}
75
76impl MaskedIpAddr {
77    /// Creates a new `MaskedIpAddr` from components.
78    ///
79    /// Do not pass an `addr` with bits set to the right of the netmask if you
80    /// intend to insert this into a postgres `cidr` field.
81    ///
82    /// # Panics
83    ///
84    /// Panics if the CIDR is greater than 32 for an [IPv4 address], or is
85    /// greater than 128 for an [IPv6 address].
86    ///
87    /// [IPv4 address]: https://doc.rust-lang.org/std/net/enum.IpAddr.html#variant.V4
88    /// [IPv6 address]: https://doc.rust-lang.org/std/net/enum.IpAddr.html#variant.V6
89    ///
90    /// # Examples
91    ///
92    /// To represent an address:
93    ///
94    /// ```rust
95    /// # use postgres_inet::MaskedIpAddr;
96    /// # use std::net::Ipv4Addr;
97    /// let ip = Ipv4Addr::new(192, 0, 2, 142);
98    /// MaskedIpAddr::new(ip, 32);
99    /// ```
100    ///
101    /// To represent a network:
102    ///
103    /// ```rust
104    /// # use postgres_inet::MaskedIpAddr;
105    /// # use std::net::Ipv6Addr;
106    /// let network = Ipv6Addr::new(0x2001, 0x0DB8, 0, 0, 0, 0, 0, 0);
107    /// MaskedIpAddr::new(network, 32);
108    /// ```
109    pub fn new<I: Into<IpAddr>>(addr: I, cidr: u8) -> MaskedIpAddr {
110        let addr = addr.into();
111
112        if match addr {
113            IpAddr::V4(_) => cidr > IPV4_CIDR_FULL,
114            IpAddr::V6(_) => cidr > IPV6_CIDR_FULL,
115        } {
116            panic!("CIDR {} too big for {:?}!", cidr, addr);
117        }
118
119        MaskedIpAddr { addr, cidr }
120    }
121
122    /// Returns [`true`] for the special 'unspecified' address.
123    ///
124    /// See the documentation for [`Ipv4Addr::is_unspecified`][IPv4] and
125    /// [`Ipv6Addr::is_unspecified`][IPv6] for more details.
126    ///
127    /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
128    /// [IPv4]: https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html#method.is_unspecified
129    /// [IPv6]: https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html#method.is_unspecified
130    ///
131    /// # Examples
132    ///
133    /// ```rust
134    /// # use postgres_inet::MaskedIpAddr;
135    /// # use std::net::{Ipv4Addr, Ipv6Addr};
136    /// assert!(MaskedIpAddr::new(Ipv4Addr::new(0, 0, 0, 0), 32).is_unspecified());
137    /// assert!(MaskedIpAddr::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 128).is_unspecified());
138    /// ```
139    pub fn is_unspecified(&self) -> bool {
140        self.addr.is_unspecified()
141    }
142
143    /// Returns [`true`] if this is a loopback address.
144    ///
145    /// See the documentation for [`Ipv4Addr::is_loopback`][IPv4] and
146    /// [`Ipv6Addr::is_loopback`][IPv6] for more details.
147    ///
148    /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
149    /// [IPv4]: https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html#method.is_loopback
150    /// [IPv6]: https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html#method.is_loopback
151    ///
152    ///
153    /// # Examples
154    ///
155    /// ```rust
156    /// # use postgres_inet::MaskedIpAddr;
157    /// # use std::net::{Ipv4Addr, Ipv6Addr};
158    /// assert!(MaskedIpAddr::new(Ipv4Addr::new(127, 0, 0, 1), 32).is_loopback());
159    /// assert!(MaskedIpAddr::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 128).is_loopback());
160    /// ```
161    pub fn is_loopback(&self) -> bool {
162        self.addr.is_loopback()
163    }
164
165    /// Returns [`true`] if this is a multicast address.
166    ///
167    /// See the documentation for [`Ipv4Addr::is_multicast`][IPv4] and
168    /// [`Ipv6Addr::is_multicast`][IPv6] for more details.
169    ///
170    /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
171    /// [IPv4]: https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html#method.is_multicast
172    /// [IPv6]: https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html#method.is_multicast
173    ///
174    /// # Examples
175    ///
176    /// ```rust
177    /// # use postgres_inet::MaskedIpAddr;
178    /// # use std::net::{Ipv4Addr, Ipv6Addr};
179    /// assert!(MaskedIpAddr::new(Ipv4Addr::new(224, 254, 0, 0), 32).is_multicast());
180    /// assert!(MaskedIpAddr::new(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0), 128).is_multicast());
181    /// ```
182    pub fn is_multicast(&self) -> bool {
183        self.addr.is_multicast()
184    }
185
186    /// Returns [`true`] if this address is an [IPv4 address], and [`false`] otherwise.
187    ///
188    /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
189    /// [`false`]: https://doc.rust-lang.org/std/primitive.bool.html
190    /// [IPv4 address]: https://doc.rust-lang.org/std/net/enum.IpAddr.html#variant.V4
191    ///
192    /// # Examples
193    ///
194    /// ```rust
195    /// # use postgres_inet::MaskedIpAddr;
196    /// # use std::net::{Ipv4Addr, Ipv6Addr};
197    /// assert!(MaskedIpAddr::new(Ipv4Addr::new(203, 0, 113, 6), 32).is_ipv4());
198    /// assert!(!MaskedIpAddr::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 128).is_ipv4());
199    /// ```
200    pub fn is_ipv4(&self) -> bool {
201        self.addr.is_ipv4()
202    }
203
204    /// Returns [`true`] if this address is an [IPv6 address], and [`false`] otherwise.
205    ///
206    /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
207    /// [`false`]: https://doc.rust-lang.org/std/primitive.bool.html
208    /// [IPv6 address]: https://doc.rust-lang.org/std/net/enum.IpAddr.html#variant.V6
209    ///
210    /// # Examples
211    ///
212    /// ```rust
213    /// # use postgres_inet::MaskedIpAddr;
214    /// # use std::net::{Ipv4Addr, Ipv6Addr};
215    /// assert!(!MaskedIpAddr::new(Ipv4Addr::new(203, 0, 113, 6), 32).is_ipv6());
216    /// assert!(MaskedIpAddr::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 128).is_ipv6());
217    /// ```
218    pub fn is_ipv6(&self) -> bool {
219        self.addr.is_ipv6()
220    }
221
222    /// Returns the contained [IP address].
223    ///
224    /// [IP address]: https://doc.rust-lang.org/std/net/enum.IpAddr.html
225    ///
226    /// # Examples
227    ///
228    /// ```rust
229    /// # use postgres_inet::MaskedIpAddr;
230    /// # use std::net::{Ipv4Addr, Ipv6Addr};
231    /// let ip = Ipv4Addr::new(192, 0, 2, 142);
232    /// assert_eq!(MaskedIpAddr::new(ip, 32).address(), ip);
233    /// let network = Ipv6Addr::new(0x2001, 0x0DB8, 0, 0, 0, 0, 0, 0);
234    /// assert_eq!(MaskedIpAddr::new(network, 32).address(), network);
235    /// ```
236    pub fn address(&self) -> IpAddr {
237        self.addr
238    }
239
240    /// Returns the contained CIDR.
241    ///
242    /// # Examples
243    ///
244    /// ```rust
245    /// # use postgres_inet::MaskedIpAddr;
246    /// # use std::net::{Ipv4Addr, Ipv6Addr};
247    /// assert_eq!(MaskedIpAddr::new(Ipv4Addr::new(192, 0, 2, 142), 32).cidr(), 32);
248    /// assert_eq!(MaskedIpAddr::new(Ipv6Addr::new(0x2001, 0x0DB8, 0, 0, 0, 0, 0, 0), 64).cidr(), 64);
249    /// ```
250    pub fn cidr(&self) -> u8 {
251        self.cidr
252    }
253
254    /// Returns the contained CIDR.
255    ///
256    /// # Examples
257    ///
258    /// ```rust
259    /// # use postgres_inet::MaskedIpAddr;
260    /// # use std::net::{Ipv4Addr, Ipv6Addr};
261    /// assert_eq!(MaskedIpAddr::new(Ipv4Addr::new(192, 0, 2, 142), 32).netmask(), 32);
262    /// assert_eq!(MaskedIpAddr::new(Ipv6Addr::new(0x2001, 0x0DB8, 0, 0, 0, 0, 0, 0), 64).netmask(), 64);
263    /// ```
264    #[deprecated(
265        since = "0.15.2",
266        note = "Supported because of historical (and wrong) use of netmask instead of CIDR. \
267                Use MaskedIpAddr::cidr instead."
268    )]
269    pub fn netmask(&self) -> u8 {
270        self.cidr()
271    }
272
273    /// Returns the subnet mask, calculated from the CIDR.
274    pub fn subnet_mask(&self) -> u128 {
275        fn cidr2mask(full_cidr: u8, cidr: u8, max: u128) -> u128 {
276            (!((1 << (full_cidr - cidr)) - 1)) & max
277        }
278
279        match self.addr {
280            IpAddr::V4(_) => cidr2mask(IPV4_CIDR_FULL, self.cidr, u128::from(std::u32::MAX)),
281            IpAddr::V6(_) => cidr2mask(IPV6_CIDR_FULL, self.cidr, std::u128::MAX),
282        }
283    }
284
285    /// Consumes the `MaskedIpAddr`, returning the IP address and netmask.
286    ///
287    /// # Examples
288    ///
289    /// ```rust
290    /// # use postgres_inet::MaskedIpAddr;
291    /// # use std::net::Ipv4Addr;
292    /// let network = Ipv4Addr::new(198, 51, 100, 0);
293    /// assert_eq!(MaskedIpAddr::new(network, 24).into_inner(), (network.into(), 24));
294    /// ```
295    pub fn into_inner(self) -> (IpAddr, u8) {
296        (self.addr, self.cidr)
297    }
298}
299
300impl From<Ipv4Addr> for MaskedIpAddr {
301    fn from(ipv4: Ipv4Addr) -> MaskedIpAddr {
302        MaskedIpAddr {
303            addr: IpAddr::V4(ipv4),
304            cidr: IPV4_CIDR_FULL,
305        }
306    }
307}
308
309impl From<Ipv6Addr> for MaskedIpAddr {
310    fn from(ipv6: Ipv6Addr) -> MaskedIpAddr {
311        MaskedIpAddr {
312            addr: IpAddr::V6(ipv6),
313            cidr: IPV6_CIDR_FULL,
314        }
315    }
316}
317
318impl From<IpAddr> for MaskedIpAddr {
319    fn from(ip: IpAddr) -> MaskedIpAddr {
320        MaskedIpAddr {
321            cidr: match ip {
322                IpAddr::V4(_) => IPV4_CIDR_FULL,
323                IpAddr::V6(_) => IPV6_CIDR_FULL,
324            },
325            addr: ip,
326        }
327    }
328}
329
330impl From<MaskedIpAddr> for IpAddr {
331    fn from(mip: MaskedIpAddr) -> IpAddr {
332        mip.addr
333    }
334}
335
336impl From<[u8; 4]> for MaskedIpAddr {
337    fn from(octets: [u8; 4]) -> MaskedIpAddr {
338        IpAddr::from(octets).into()
339    }
340}
341
342impl From<[u8; 16]> for MaskedIpAddr {
343    fn from(octets: [u8; 16]) -> MaskedIpAddr {
344        IpAddr::from(octets).into()
345    }
346}
347
348impl From<[u16; 8]> for MaskedIpAddr {
349    fn from(segments: [u16; 8]) -> MaskedIpAddr {
350        IpAddr::from(segments).into()
351    }
352}
353
354#[cfg(feature = "ipnetwork")]
355impl From<ipnetwork::IpNetwork> for MaskedIpAddr {
356    fn from(ipnetwork: ipnetwork::IpNetwork) -> MaskedIpAddr {
357        MaskedIpAddr::new(ipnetwork.ip(), ipnetwork.prefix())
358    }
359}
360
361#[cfg(feature = "ipnetwork")]
362impl From<MaskedIpAddr> for ipnetwork::IpNetwork {
363    fn from(mip: MaskedIpAddr) -> ipnetwork::IpNetwork {
364        // this conversion will never fail
365        ipnetwork::IpNetwork::new(mip.address(), mip.cidr()).unwrap()
366    }
367}
368
369impl fmt::Display for MaskedIpAddr {
370    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
371        match self.addr {
372            IpAddr::V4(ipv4) => match self.cidr {
373                IPV4_CIDR_FULL => ipv4.fmt(f),
374                _ => write!(f, "{}/{}", ipv4, self.cidr),
375            },
376            IpAddr::V6(ipv6) => match self.cidr {
377                IPV6_CIDR_FULL => ipv6.fmt(f),
378                _ => write!(f, "{}/{}", ipv6, self.cidr),
379            },
380        }
381    }
382}
383
384impl fmt::Debug for MaskedIpAddr {
385    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
386        write!(f, "{}/{}", self.addr, self.cidr)
387    }
388}
389
390impl FromSql<'_> for MaskedIpAddr {
391    fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + 'static + Sync + Send>> {
392        // The address family is at raw[0], as AF_INET for ipv4 or (AF_INET + 1)
393        // for ipv6.  It's unneeded, as `nb` at raw[3] tells us the version just as
394        // well. A bool of the `cidr`ness is at raw[2]. It's also unneeded, as it
395        // doesn't affect our codepath in any way whatsoever.
396
397        macro_rules! copy_address {
398            ($num_bytes:expr) => {{
399                const NUM_BYTES: usize = $num_bytes;
400
401                let mut octets = [0u8; NUM_BYTES];
402                octets.copy_from_slice(&raw[4..(NUM_BYTES + 4)]);
403                IpAddr::from(octets)
404            }};
405        }
406
407        Ok(MaskedIpAddr {
408            addr: match raw[3] {
409                IPV4_ADDRESS_SIZE => copy_address!(IPV4_ADDRESS_SIZE as usize),
410                IPV6_ADDRESS_SIZE => copy_address!(IPV6_ADDRESS_SIZE as usize),
411                _ => panic!("Unknown Internet Protocol Version!"),
412            },
413            cidr: raw[1],
414        })
415    }
416
417    fn accepts(ty: &Type) -> bool {
418        *ty == Type::INET || *ty == Type::CIDR
419    }
420}
421
422impl ToSql for MaskedIpAddr {
423    fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
424        fn address_size(addr: IpAddr) -> u8 {
425            match addr {
426                IpAddr::V4(_) => IPV4_ADDRESS_SIZE,
427                IpAddr::V6(_) => IPV6_ADDRESS_SIZE,
428            }
429        }
430
431        // Reserve Expected Additional Capacity
432        w.reserve(4 + address_size(self.addr) as usize);
433
434        // Send the Address Header
435        w.extend_from_slice(&[
436            // Address Family
437            match self.addr {
438                IpAddr::V4(_) => IPV4_ADDRESS_FAMILY,
439                IpAddr::V6(_) => IPV6_ADDRESS_FAMILY,
440            },
441            // Network Mask
442            self.cidr,
443            // Is this a CIDR?
444            (*ty == Type::CIDR) as u8,
445            // Address Size
446            address_size(self.addr),
447        ]);
448
449        // Send the actual Address
450        match self.addr {
451            IpAddr::V4(ipv4) => w.extend_from_slice(&ipv4.octets()),
452            IpAddr::V6(ipv6) => w.extend_from_slice(&ipv6.octets()),
453        };
454
455        Ok(IsNull::No)
456    }
457
458    fn accepts(ty: &Type) -> bool {
459        *ty == Type::INET || *ty == Type::CIDR
460    }
461
462    to_sql_checked!();
463}
464
465/// An error which can be returned when parsing a [`MaskedIpAddr`].
466///
467/// [`MaskedIpAddr`]: struct.MaskedIpAddr.html
468#[derive(Clone, Debug, PartialEq, Eq)]
469pub enum MaskedIpAddrParseError {
470    /// An error occured in parsing the IP address
471    Address(AddrParseError),
472    /// An error occured in parsing the netmask
473    Netmask(ParseIntError),
474    /// An error occured elsewhere in parsing
475    Format,
476}
477
478impl fmt::Display for MaskedIpAddrParseError {
479    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
480        match *self {
481            MaskedIpAddrParseError::Address(ref e) => e.fmt(f),
482            MaskedIpAddrParseError::Netmask(ref e) => e.fmt(f),
483            MaskedIpAddrParseError::Format => f.write_str("invalid CIDR syntax"),
484        }
485    }
486}
487
488impl Error for MaskedIpAddrParseError {
489    fn cause(&self) -> Option<&dyn Error> {
490        match *self {
491            MaskedIpAddrParseError::Address(ref err) => Some(err),
492            MaskedIpAddrParseError::Netmask(ref err) => Some(err),
493            MaskedIpAddrParseError::Format => None,
494        }
495    }
496}
497
498impl From<AddrParseError> for MaskedIpAddrParseError {
499    fn from(from: AddrParseError) -> MaskedIpAddrParseError {
500        MaskedIpAddrParseError::Address(from)
501    }
502}
503
504impl From<ParseIntError> for MaskedIpAddrParseError {
505    fn from(from: ParseIntError) -> MaskedIpAddrParseError {
506        MaskedIpAddrParseError::Netmask(from)
507    }
508}
509
510impl FromStr for MaskedIpAddr {
511    type Err = MaskedIpAddrParseError;
512
513    fn from_str(s: &str) -> Result<Self, Self::Err> {
514        let parts: Vec<&str> = s.split('/').collect();
515        match &parts[..] {
516            [ip] => Ok(IpAddr::from_str(ip)?.into()),
517            [ip, cidr] => Ok(MaskedIpAddr::new(IpAddr::from_str(ip)?, cidr.parse()?)),
518            _ => Err(MaskedIpAddrParseError::Format),
519        }
520    }
521}