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}