#![doc(html_root_url = "https://docs.rs/postgres-inet/0.15.1")]
#![deny(
missing_copy_implementations, missing_debug_implementations, missing_docs, trivial_casts,
trivial_numeric_casts, unsafe_code, unstable_features, unused_extern_crates,
unused_import_braces, unused_qualifications, unused_results
)]
#[cfg(feature = "ipnetwork")]
extern crate ipnetwork;
#[macro_use]
extern crate postgres;
mod tests;
use postgres::types::{self, FromSql, IsNull, ToSql, Type};
use std::error::Error;
use std::fmt;
use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr};
use std::num::ParseIntError;
use std::str::FromStr;
const IPV4_NETMASK_FULL: u8 = 32;
const IPV4_ADDRESS_FAMILY: u8 = 2; const IPV4_ADDRESS_SIZE: u8 = 4;
const IPV6_NETMASK_FULL: u8 = 128;
const IPV6_ADDRESS_FAMILY: u8 = IPV4_ADDRESS_FAMILY + 1;
const IPV6_ADDRESS_SIZE: u8 = 16;
#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct MaskedIpAddr {
addr: IpAddr,
mask: u8,
}
impl MaskedIpAddr {
pub fn new<I: Into<IpAddr>>(addr: I, mask: u8) -> MaskedIpAddr {
let addr = addr.into();
if match addr {
IpAddr::V4(_) => mask > IPV4_NETMASK_FULL,
IpAddr::V6(_) => mask > IPV6_NETMASK_FULL,
} {
panic!("Mask {} too big for {:?}!", mask, addr);
}
MaskedIpAddr { addr, mask }
}
pub fn is_unspecified(&self) -> bool {
self.addr.is_unspecified()
}
pub fn is_loopback(&self) -> bool {
self.addr.is_loopback()
}
pub fn is_multicast(&self) -> bool {
self.addr.is_multicast()
}
pub fn is_ipv4(&self) -> bool {
self.addr.is_ipv4()
}
pub fn is_ipv6(&self) -> bool {
self.addr.is_ipv6()
}
pub fn address(&self) -> IpAddr {
self.addr
}
pub fn netmask(&self) -> u8 {
self.mask
}
pub fn into_inner(self) -> (IpAddr, u8) {
(self.addr, self.mask)
}
}
impl From<Ipv4Addr> for MaskedIpAddr {
fn from(ipv4: Ipv4Addr) -> MaskedIpAddr {
MaskedIpAddr {
addr: IpAddr::V4(ipv4),
mask: IPV4_NETMASK_FULL,
}
}
}
impl From<Ipv6Addr> for MaskedIpAddr {
fn from(ipv6: Ipv6Addr) -> MaskedIpAddr {
MaskedIpAddr {
addr: IpAddr::V6(ipv6),
mask: IPV6_NETMASK_FULL,
}
}
}
impl From<IpAddr> for MaskedIpAddr {
fn from(ip: IpAddr) -> MaskedIpAddr {
MaskedIpAddr {
mask: match ip {
IpAddr::V4(_) => IPV4_NETMASK_FULL,
IpAddr::V6(_) => IPV6_NETMASK_FULL,
},
addr: ip,
}
}
}
impl From<MaskedIpAddr> for IpAddr {
fn from(mip: MaskedIpAddr) -> IpAddr {
mip.addr
}
}
impl From<[u8; 4]> for MaskedIpAddr {
fn from(octets: [u8; 4]) -> MaskedIpAddr {
IpAddr::from(octets).into()
}
}
impl From<[u8; 16]> for MaskedIpAddr {
fn from(octets: [u8; 16]) -> MaskedIpAddr {
IpAddr::from(octets).into()
}
}
impl From<[u16; 8]> for MaskedIpAddr {
fn from(segments: [u16; 8]) -> MaskedIpAddr {
IpAddr::from(segments).into()
}
}
#[cfg(feature = "ipnetwork")]
impl From<ipnetwork::IpNetwork> for MaskedIpAddr {
fn from(ipnetwork: ipnetwork::IpNetwork) -> MaskedIpAddr {
MaskedIpAddr::new(ipnetwork.ip(), ipnetwork.prefix())
}
}
#[cfg(feature = "ipnetwork")]
impl From<MaskedIpAddr> for ipnetwork::IpNetwork {
fn from(mip: MaskedIpAddr) -> ipnetwork::IpNetwork {
ipnetwork::IpNetwork::new(mip.address(), mip.netmask()).unwrap()
}
}
impl fmt::Display for MaskedIpAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.addr {
IpAddr::V4(ipv4) => match self.mask {
IPV4_NETMASK_FULL => ipv4.fmt(f),
_ => write!(f, "{}/{}", ipv4, self.mask),
},
IpAddr::V6(ipv6) => match self.mask {
IPV6_NETMASK_FULL => ipv6.fmt(f),
_ => write!(f, "{}/{}", ipv6, self.mask),
},
}
}
}
impl fmt::Debug for MaskedIpAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}/{}", self.addr, self.mask)
}
}
impl FromSql for MaskedIpAddr {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + 'static + Sync + Send>> {
Ok(MaskedIpAddr {
addr: match raw[3] {
IPV4_ADDRESS_SIZE => IpAddr::V4(Ipv4Addr::new(raw[4], raw[5], raw[6], raw[7])),
IPV6_ADDRESS_SIZE => {
let mut octets = [0u8; IPV6_ADDRESS_SIZE as usize];
octets.copy_from_slice(&raw[4..20]);
IpAddr::V6(Ipv6Addr::from(octets))
}
_ => panic!("Unknown Internet Protocol Version!"),
},
mask: raw[1],
})
}
accepts!(types::CIDR, types::INET);
}
impl ToSql for MaskedIpAddr {
fn to_sql(&self, ty: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
w.extend_from_slice(&[
match self.addr {
IpAddr::V4(_) => IPV4_ADDRESS_FAMILY,
IpAddr::V6(_) => IPV6_ADDRESS_FAMILY,
},
self.mask,
(*ty == types::CIDR) as u8,
match self.addr {
IpAddr::V4(_) => IPV4_ADDRESS_SIZE,
IpAddr::V6(_) => IPV6_ADDRESS_SIZE,
},
]);
match self.addr {
IpAddr::V4(ipv4) => w.extend_from_slice(&ipv4.octets()),
IpAddr::V6(ipv6) => w.extend_from_slice(&ipv6.octets()),
};
Ok(IsNull::No)
}
accepts!(types::CIDR, types::INET);
to_sql_checked!();
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MaskedIpAddrParseError {
Address(AddrParseError),
Netmask(ParseIntError),
Format,
}
impl fmt::Display for MaskedIpAddrParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
MaskedIpAddrParseError::Address(ref e) => e.fmt(f),
MaskedIpAddrParseError::Netmask(ref e) => e.fmt(f),
MaskedIpAddrParseError::Format => f.write_str(self.description()),
}
}
}
impl Error for MaskedIpAddrParseError {
fn description(&self) -> &str {
"invalid IP address/netmask syntax"
}
fn cause(&self) -> Option<&Error> {
match *self {
MaskedIpAddrParseError::Address(ref err) => Some(err),
MaskedIpAddrParseError::Netmask(ref err) => Some(err),
MaskedIpAddrParseError::Format => None,
}
}
}
impl From<AddrParseError> for MaskedIpAddrParseError {
fn from(from: AddrParseError) -> MaskedIpAddrParseError {
MaskedIpAddrParseError::Address(from)
}
}
impl From<ParseIntError> for MaskedIpAddrParseError {
fn from(from: ParseIntError) -> MaskedIpAddrParseError {
MaskedIpAddrParseError::Netmask(from)
}
}
impl FromStr for MaskedIpAddr {
type Err = MaskedIpAddrParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split('/').collect();
match parts.len() {
1 => Ok(IpAddr::from_str(parts[0])?.into()),
2 => Ok(MaskedIpAddr::new(
IpAddr::from_str(parts[0])?,
parts[1].parse()?,
)),
_ => Err(MaskedIpAddrParseError::Format),
}
}
}