Skip to main content

diesel/pg/types/
network_address.rs

1extern crate ipnetwork;
2extern crate libc;
3
4use self::ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
5use std::io::prelude::*;
6use std::net::{Ipv4Addr, Ipv6Addr};
7
8use crate::deserialize::{self, FromSql, FromSqlRow};
9use crate::pg::{Pg, PgValue};
10#[cfg(test)]
11use crate::query_builder::bind_collector::ByteWrapper;
12use crate::serialize::{self, IsNull, Output, ToSql};
13use crate::sql_types::{Cidr, Inet};
14
15#[cfg(windows)]
16const AF_INET: u8 = 2;
17// Maybe not used, but defining to follow Rust's libstd/net/sys
18#[cfg(target_os = "redox")]
19const AF_INET: u8 = 1;
20
21#[allow(clippy::cast_possible_truncation)] // it's 2
22#[cfg(not(any(windows, target_os = "redox")))]
23const AF_INET: u8 = libc::AF_INET as u8;
24
25const PGSQL_AF_INET: u8 = AF_INET;
26const PGSQL_AF_INET6: u8 = AF_INET + 1;
27
28#[allow(dead_code)]
29mod foreign_derives {
30    use super::*;
31    use crate::expression::AsExpression;
32
33    #[derive(const _: () =
    {
        use diesel;
        impl<'__expr> diesel::expression::AsExpression<Inet> for
            &'__expr IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Inet, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Inet>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>
            for &'__expr IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Inet>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2> diesel::expression::AsExpression<Inet> for
            &'__expr2 &'__expr IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Inet, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Inet>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>
            for &'__expr2 &'__expr IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Inet>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl<__DB>
            diesel::serialize::ToSql<diesel::sql_types::Nullable<Inet>, __DB>
            for IpNetwork where __DB: diesel::backend::Backend,
            Self: diesel::serialize::ToSql<Inet, __DB> {
            fn to_sql<'__b>(&'__b self,
                out: &mut diesel::serialize::Output<'__b, '_, __DB>)
                -> diesel::serialize::Result {
                diesel::serialize::ToSql::<Inet, __DB>::to_sql(self, out)
            }
        }
        impl diesel::expression::AsExpression<Inet> for IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Inet, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Inet>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>
            for IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Inet>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl<'__expr> diesel::expression::AsExpression<Cidr> for
            &'__expr IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Cidr, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Cidr>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>
            for &'__expr IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Cidr>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2> diesel::expression::AsExpression<Cidr> for
            &'__expr2 &'__expr IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Cidr, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Cidr>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>
            for &'__expr2 &'__expr IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Cidr>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl<__DB>
            diesel::serialize::ToSql<diesel::sql_types::Nullable<Cidr>, __DB>
            for IpNetwork where __DB: diesel::backend::Backend,
            Self: diesel::serialize::ToSql<Cidr, __DB> {
            fn to_sql<'__b>(&'__b self,
                out: &mut diesel::serialize::Output<'__b, '_, __DB>)
                -> diesel::serialize::Result {
                diesel::serialize::ToSql::<Cidr, __DB>::to_sql(self, out)
            }
        }
        impl diesel::expression::AsExpression<Cidr> for IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Cidr, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Cidr>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>
            for IpNetwork {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Cidr>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
    };AsExpression, const _: () =
    {
        use diesel;
        impl<__DB, __ST> diesel::deserialize::Queryable<__ST, __DB> for
            IpNetwork where __DB: diesel::backend::Backend,
            __ST: diesel::sql_types::SingleValue,
            Self: diesel::deserialize::FromSql<__ST, __DB> {
            type Row = Self;
            fn build(row: Self) -> diesel::deserialize::Result<Self> {
                diesel::deserialize::Result::Ok(row)
            }
        }
    };FromSqlRow)]
34    #[diesel(foreign_derive)]
35    #[diesel(sql_type = Inet)]
36    #[diesel(sql_type = Cidr)]
37    struct IpNetworkProxy(IpNetwork);
38}
39
40macro_rules! err {
41    () => {
42        Err("invalid network address format".into())
43    };
44    ($msg:expr) => {
45        Err(format!("invalid network address format. {}", $msg).into())
46    };
47}
48
49macro_rules! assert_or_error {
50    ($cond:expr) => {
51        if !$cond {
52            return err!();
53        }
54    };
55
56    ($cond:expr, $msg:expr) => {
57        if !$cond {
58            return err!($msg);
59        }
60    };
61}
62
63macro_rules! impl_Sql {
64    ($ty: ty, $net_type: expr) => {
65        #[cfg(all(feature = "postgres_backend", feature = "network-address"))]
66        impl FromSql<$ty, Pg> for IpNetwork {
67            fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
68                // https://github.com/postgres/postgres/blob/55c3391d1e6a201b5b891781d21fe682a8c64fe6/src/include/utils/inet.h#L23-L28
69                let bytes = value.as_bytes();
70                assert_or_error!(4 <= bytes.len(), "input is too short.");
71                let af = bytes[0];
72                let prefix = bytes[1];
73                let net_type = bytes[2];
74                let len = bytes[3];
75                assert_or_error!(
76                    net_type == $net_type,
77                    format!("returned type isn't a {}", stringify!($ty))
78                );
79                if af == PGSQL_AF_INET {
80                    assert_or_error!(bytes.len() == 8);
81                    assert_or_error!(len == 4, "the data isn't the size of ipv4");
82                    let b = &bytes[4..];
83                    let addr = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
84                    let inet = Ipv4Network::new(addr, prefix)?;
85                    Ok(IpNetwork::V4(inet))
86                } else if af == PGSQL_AF_INET6 {
87                    assert_or_error!(bytes.len() == 20);
88                    assert_or_error!(len == 16, "the data isn't the size of ipv6");
89                    let b = &bytes[4..];
90                    let addr = Ipv6Addr::from([
91                        b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11],
92                        b[12], b[13], b[14], b[15],
93                    ]);
94                    let inet = Ipv6Network::new(addr, prefix)?;
95                    Ok(IpNetwork::V6(inet))
96                } else {
97                    err!()
98                }
99            }
100        }
101
102        #[cfg(all(feature = "postgres_backend", feature = "network-address"))]
103        impl ToSql<$ty, Pg> for IpNetwork {
104            fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
105                use self::ipnetwork::IpNetwork::*;
106                let net_type = $net_type;
107                match *self {
108                    V4(ref net) => {
109                        let mut data = [0u8; 8];
110                        let af = PGSQL_AF_INET;
111                        let prefix = net.prefix();
112                        let len: u8 = 4;
113                        let addr = net.ip().octets();
114                        data[0] = af;
115                        data[1] = prefix;
116                        data[2] = net_type;
117                        data[3] = len;
118                        data[4..].copy_from_slice(&addr);
119                        out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
120                    }
121                    V6(ref net) => {
122                        let mut data = [0u8; 20];
123                        let af = PGSQL_AF_INET6;
124                        let prefix = net.prefix();
125                        let len: u8 = 16;
126                        let addr = net.ip().octets();
127                        data[0] = af;
128                        data[1] = prefix;
129                        data[2] = net_type;
130                        data[3] = len;
131                        data[4..].copy_from_slice(&addr);
132                        out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
133                    }
134                }
135            }
136        }
137    };
138}
139
140impl FromSql<Inet, Pg> for IpNetwork {
    fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
        let bytes = value.as_bytes();
        if !(4 <= bytes.len()) {
            return Err(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                        "input is too short."))
                            }).into());
        };
        let af = bytes[0];
        let prefix = bytes[1];
        let net_type = bytes[2];
        let len = bytes[3];
        if !(net_type == 0) {
            return Err(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                        ::alloc::__export::must_use({
                                                ::alloc::fmt::format(format_args!("returned type isn\'t a {0}",
                                                        "Inet"))
                                            })))
                            }).into());
        };
        if af == PGSQL_AF_INET {
            if !(bytes.len() == 8) {
                return Err("invalid network address format".into());
            };
            if !(len == 4) {
                return Err(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                            "the data isn't the size of ipv4"))
                                }).into());
            };
            let b = &bytes[4..];
            let addr = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
            let inet = Ipv4Network::new(addr, prefix)?;
            Ok(IpNetwork::V4(inet))
        } else if af == PGSQL_AF_INET6 {
            if !(bytes.len() == 20) {
                return Err("invalid network address format".into());
            };
            if !(len == 16) {
                return Err(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                            "the data isn't the size of ipv6"))
                                }).into());
            };
            let b = &bytes[4..];
            let addr =
                Ipv6Addr::from([b[0], b[1], b[2], b[3], b[4], b[5], b[6],
                            b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14],
                            b[15]]);
            let inet = Ipv6Network::new(addr, prefix)?;
            Ok(IpNetwork::V6(inet))
        } else { Err("invalid network address format".into()) }
    }
}
impl ToSql<Inet, Pg> for IpNetwork {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        use self::ipnetwork::IpNetwork::*;
        let net_type = 0;
        match *self {
            V4(ref net) => {
                let mut data = [0u8; 8];
                let af = PGSQL_AF_INET;
                let prefix = net.prefix();
                let len: u8 = 4;
                let addr = net.ip().octets();
                data[0] = af;
                data[1] = prefix;
                data[2] = net_type;
                data[3] = len;
                data[4..].copy_from_slice(&addr);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
            V6(ref net) => {
                let mut data = [0u8; 20];
                let af = PGSQL_AF_INET6;
                let prefix = net.prefix();
                let len: u8 = 16;
                let addr = net.ip().octets();
                data[0] = af;
                data[1] = prefix;
                data[2] = net_type;
                data[3] = len;
                data[4..].copy_from_slice(&addr);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
        }
    }
}impl_Sql!(Inet, 0);
141impl FromSql<Cidr, Pg> for IpNetwork {
    fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
        let bytes = value.as_bytes();
        if !(4 <= bytes.len()) {
            return Err(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                        "input is too short."))
                            }).into());
        };
        let af = bytes[0];
        let prefix = bytes[1];
        let net_type = bytes[2];
        let len = bytes[3];
        if !(net_type == 1) {
            return Err(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                        ::alloc::__export::must_use({
                                                ::alloc::fmt::format(format_args!("returned type isn\'t a {0}",
                                                        "Cidr"))
                                            })))
                            }).into());
        };
        if af == PGSQL_AF_INET {
            if !(bytes.len() == 8) {
                return Err("invalid network address format".into());
            };
            if !(len == 4) {
                return Err(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                            "the data isn't the size of ipv4"))
                                }).into());
            };
            let b = &bytes[4..];
            let addr = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
            let inet = Ipv4Network::new(addr, prefix)?;
            Ok(IpNetwork::V4(inet))
        } else if af == PGSQL_AF_INET6 {
            if !(bytes.len() == 20) {
                return Err("invalid network address format".into());
            };
            if !(len == 16) {
                return Err(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                            "the data isn't the size of ipv6"))
                                }).into());
            };
            let b = &bytes[4..];
            let addr =
                Ipv6Addr::from([b[0], b[1], b[2], b[3], b[4], b[5], b[6],
                            b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14],
                            b[15]]);
            let inet = Ipv6Network::new(addr, prefix)?;
            Ok(IpNetwork::V6(inet))
        } else { Err("invalid network address format".into()) }
    }
}
impl ToSql<Cidr, Pg> for IpNetwork {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        use self::ipnetwork::IpNetwork::*;
        let net_type = 1;
        match *self {
            V4(ref net) => {
                let mut data = [0u8; 8];
                let af = PGSQL_AF_INET;
                let prefix = net.prefix();
                let len: u8 = 4;
                let addr = net.ip().octets();
                data[0] = af;
                data[1] = prefix;
                data[2] = net_type;
                data[3] = len;
                data[4..].copy_from_slice(&addr);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
            V6(ref net) => {
                let mut data = [0u8; 20];
                let af = PGSQL_AF_INET6;
                let prefix = net.prefix();
                let len: u8 = 16;
                let addr = net.ip().octets();
                data[0] = af;
                data[1] = prefix;
                data[2] = net_type;
                data[3] = len;
                data[4..].copy_from_slice(&addr);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
        }
    }
}impl_Sql!(Cidr, 1);
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146    use crate::test_helpers::format_error;
147
148    #[diesel_test_helper::test]
149    fn v4address_to_sql() {
150        macro_rules! test_to_sql {
151            ($ty:ty, $net_type:expr) => {
152                let mut buffer = Vec::new();
153                {
154                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
155                    let test_address =
156                        IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
157                    ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
158                }
159                assert_eq!(buffer, vec![PGSQL_AF_INET, 32, $net_type, 4, 127, 0, 0, 1]);
160            };
161        }
162
163        test_to_sql!(Inet, 0);
164        test_to_sql!(Cidr, 1);
165    }
166
167    #[diesel_test_helper::test]
168    fn some_v4address_from_sql() {
169        macro_rules! test_some_address_from_sql {
170            ($ty:tt) => {
171                let input_address =
172                    IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
173                let mut buffer = Vec::new();
174                {
175                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
176                    ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
177                }
178                let output_address =
179                    FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
180                assert_eq!(input_address, output_address);
181            };
182        }
183
184        test_some_address_from_sql!(Cidr);
185        test_some_address_from_sql!(Inet);
186    }
187
188    #[diesel_test_helper::test]
189    fn v6address_to_sql() {
190        macro_rules! test_to_sql {
191            ($ty:ty, $net_type:expr) => {
192                let mut buffer = Vec::new();
193                {
194                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
195                    let test_address = IpNetwork::V6(
196                        Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 64).unwrap(),
197                    );
198                    ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
199                }
200                assert_eq!(
201                    buffer,
202                    vec![
203                        PGSQL_AF_INET6,
204                        64,
205                        $net_type,
206                        16,
207                        0,
208                        0,
209                        0,
210                        0,
211                        0,
212                        0,
213                        0,
214                        0,
215                        0,
216                        0,
217                        0,
218                        0,
219                        0,
220                        0,
221                        0,
222                        1,
223                    ]
224                );
225            };
226        }
227
228        test_to_sql!(Inet, 0);
229        test_to_sql!(Cidr, 1);
230    }
231
232    #[diesel_test_helper::test]
233    fn some_v6address_from_sql() {
234        macro_rules! test_some_address_from_sql {
235            ($ty:tt) => {
236                let input_address = IpNetwork::V6(
237                    Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 64).unwrap(),
238                );
239                let mut buffer = Vec::new();
240                {
241                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
242                    ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
243                }
244                let output_address =
245                    FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
246                assert_eq!(input_address, output_address);
247            };
248        }
249
250        test_some_address_from_sql!(Inet);
251        test_some_address_from_sql!(Cidr);
252    }
253
254    #[diesel_test_helper::test]
255    fn bad_address_from_sql() {
256        macro_rules! bad_address_from_sql {
257            ($ty:tt) => {
258                let address: Result<IpNetwork, _> =
259                    FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&[7, PGSQL_AF_INET, 0]));
260                assert_eq!(
261                    format_error(&*address.unwrap_err()),
262                    "invalid network address format. input is too short."
263                );
264            };
265        }
266
267        bad_address_from_sql!(Inet);
268        bad_address_from_sql!(Cidr);
269    }
270
271    #[diesel_test_helper::test]
272    fn no_address_from_sql() {
273        macro_rules! test_no_address_from_sql {
274            ($ty:ty) => {
275                let address: Result<IpNetwork, _> = FromSql::<$ty, Pg>::from_nullable_sql(None);
276                assert_eq!(
277                    format_error(&*address.unwrap_err()),
278                    "Unexpected null for non-null column"
279                );
280            };
281        }
282
283        test_no_address_from_sql!(Inet);
284        test_no_address_from_sql!(Cidr);
285    }
286}