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(AsExpression, 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_Sql!(Inet, 0);
141impl_Sql!(Cidr, 1);
142
143#[test]
144fn v4address_to_sql() {
145    macro_rules! test_to_sql {
146        ($ty:ty, $net_type:expr) => {
147            let mut buffer = Vec::new();
148            {
149                let mut bytes = Output::test(ByteWrapper(&mut buffer));
150                let test_address =
151                    IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
152                ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
153            }
154            assert_eq!(buffer, vec![PGSQL_AF_INET, 32, $net_type, 4, 127, 0, 0, 1]);
155        };
156    }
157
158    test_to_sql!(Inet, 0);
159    test_to_sql!(Cidr, 1);
160}
161
162#[test]
163fn some_v4address_from_sql() {
164    macro_rules! test_some_address_from_sql {
165        ($ty:tt) => {
166            let input_address =
167                IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
168            let mut buffer = Vec::new();
169            {
170                let mut bytes = Output::test(ByteWrapper(&mut buffer));
171                ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
172            }
173            let output_address = FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
174            assert_eq!(input_address, output_address);
175        };
176    }
177
178    test_some_address_from_sql!(Cidr);
179    test_some_address_from_sql!(Inet);
180}
181
182#[test]
183fn v6address_to_sql() {
184    macro_rules! test_to_sql {
185        ($ty:ty, $net_type:expr) => {
186            let mut buffer = Vec::new();
187            {
188                let mut bytes = Output::test(ByteWrapper(&mut buffer));
189                let test_address = IpNetwork::V6(
190                    Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 64).unwrap(),
191                );
192                ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
193            }
194            assert_eq!(
195                buffer,
196                vec![
197                    PGSQL_AF_INET6,
198                    64,
199                    $net_type,
200                    16,
201                    0,
202                    0,
203                    0,
204                    0,
205                    0,
206                    0,
207                    0,
208                    0,
209                    0,
210                    0,
211                    0,
212                    0,
213                    0,
214                    0,
215                    0,
216                    1,
217                ]
218            );
219        };
220    }
221
222    test_to_sql!(Inet, 0);
223    test_to_sql!(Cidr, 1);
224}
225
226#[test]
227fn some_v6address_from_sql() {
228    macro_rules! test_some_address_from_sql {
229        ($ty:tt) => {
230            let input_address =
231                IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 64).unwrap());
232            let mut buffer = Vec::new();
233            {
234                let mut bytes = Output::test(ByteWrapper(&mut buffer));
235                ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
236            }
237            let output_address = FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
238            assert_eq!(input_address, output_address);
239        };
240    }
241
242    test_some_address_from_sql!(Inet);
243    test_some_address_from_sql!(Cidr);
244}
245
246#[test]
247fn bad_address_from_sql() {
248    macro_rules! bad_address_from_sql {
249        ($ty:tt) => {
250            let address: Result<IpNetwork, _> =
251                FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&[7, PGSQL_AF_INET, 0]));
252            assert_eq!(
253                address.unwrap_err().to_string(),
254                "invalid network address format. input is too short."
255            );
256        };
257    }
258
259    bad_address_from_sql!(Inet);
260    bad_address_from_sql!(Cidr);
261}
262
263#[test]
264fn no_address_from_sql() {
265    macro_rules! test_no_address_from_sql {
266        ($ty:ty) => {
267            let address: Result<IpNetwork, _> = FromSql::<$ty, Pg>::from_nullable_sql(None);
268            assert_eq!(
269                address.unwrap_err().to_string(),
270                "Unexpected null for non-null column"
271            );
272        };
273    }
274
275    test_no_address_from_sql!(Inet);
276    test_no_address_from_sql!(Cidr);
277}