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#[cfg(target_os = "redox")]
18const AF_INET: u8 = 1;
19
20#[allow(clippy::cast_possible_truncation)] #[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 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}