diesel/pg/types/
ipnet_address.rs

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