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#[cfg(test)]
150mod tests {
151 use super::*;
152 #[diesel_test_helper::test]
153 fn v4address_to_sql() {
154 macro_rules! test_to_sql {
155 ($ty:ty, $net_type:expr) => {
156 let mut buffer = Vec::new();
157 {
158 let mut bytes = Output::test(ByteWrapper(&mut buffer));
159 let test_address =
160 IpNet::V4(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
161 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
162 }
163 assert_eq!(buffer, vec![PGSQL_AF_INET, 32, $net_type, 4, 127, 0, 0, 1]);
164 };
165 }
166
167 test_to_sql!(Inet, 0);
168 test_to_sql!(Cidr, 1);
169 }
170
171 #[diesel_test_helper::test]
172 fn v4_masked_address_to_sql() {
173 macro_rules! test_to_sql {
174 ($ty:ty, $net_type:expr, $last_byte:expr) => {
175 let mut buffer = Vec::new();
176 {
177 let mut bytes = Output::test(ByteWrapper(&mut buffer));
178 let test_address =
179 IpNet::V4(Ipv4Net::new(Ipv4Addr::new(192, 168, 0, 1), 24).unwrap());
180 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
181 }
182 assert_eq!(
183 buffer,
184 vec![PGSQL_AF_INET, 24, $net_type, 4, 192, 168, 0, $last_byte]
185 );
186 };
187 }
188
189 test_to_sql!(Inet, 0, 1);
190 test_to_sql!(Cidr, 1, 0);
191 }
192
193 #[diesel_test_helper::test]
194 fn some_v4address_from_sql() {
195 macro_rules! test_some_address_from_sql {
196 ($ty:tt) => {
197 let input_address =
198 IpNet::V4(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
199 let mut buffer = Vec::new();
200 {
201 let mut bytes = Output::test(ByteWrapper(&mut buffer));
202 ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
203 }
204 let output_address =
205 FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
206 assert_eq!(input_address, output_address);
207 };
208 }
209
210 test_some_address_from_sql!(Cidr);
211 test_some_address_from_sql!(Inet);
212 }
213
214 #[diesel_test_helper::test]
215 fn v6address_to_sql() {
216 macro_rules! test_to_sql {
217 ($ty:ty, $net_type:expr) => {
218 let mut buffer = Vec::new();
219 {
220 let mut bytes = Output::test(ByteWrapper(&mut buffer));
221 let test_address = IpNet::V6(
222 Ipv6Net::new(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 0), 64).unwrap(),
223 );
224 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
225 }
226 assert_eq!(
227 buffer,
228 vec![
229 PGSQL_AF_INET6,
230 64,
231 $net_type,
232 16,
233 0,
234 0xfd,
235 0,
236 0,
237 0,
238 0,
239 0,
240 0,
241 0,
242 0,
243 0,
244 0,
245 0,
246 0,
247 0,
248 0,
249 ]
250 );
251 };
252 }
253
254 test_to_sql!(Inet, 0);
255 test_to_sql!(Cidr, 1);
256 }
257
258 #[diesel_test_helper::test]
259 fn v6_masked_address_from_sql() {
260 macro_rules! test_to_sql {
261 ($ty:ty, $net_type:expr, $last_byte:expr) => {
262 let mut buffer = Vec::new();
263 {
264 let mut bytes = Output::test(ByteWrapper(&mut buffer));
265 let test_address = IpNet::V6(
266 Ipv6Net::new(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 1), 64).unwrap(),
267 );
268 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
269 }
270 assert_eq!(
271 buffer,
272 vec![
273 PGSQL_AF_INET6,
274 64,
275 $net_type,
276 16,
277 0,
278 0xfd,
279 0,
280 0,
281 0,
282 0,
283 0,
284 0,
285 0,
286 0,
287 0,
288 0,
289 0,
290 0,
291 0,
292 $last_byte,
293 ]
294 );
295 };
296 }
297
298 test_to_sql!(Inet, 0, 1);
299 test_to_sql!(Cidr, 1, 0);
300 }
301
302 #[diesel_test_helper::test]
303 fn some_v6address_from_sql() {
304 macro_rules! test_some_address_from_sql {
305 ($ty:tt) => {
306 let input_address =
307 IpNet::V6(Ipv6Net::new(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 0), 64).unwrap());
308 let mut buffer = Vec::new();
309 {
310 let mut bytes = Output::test(ByteWrapper(&mut buffer));
311 ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
312 }
313 let output_address =
314 FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
315 assert_eq!(input_address, output_address);
316 };
317 }
318
319 test_some_address_from_sql!(Inet);
320 test_some_address_from_sql!(Cidr);
321 }
322
323 #[diesel_test_helper::test]
324 fn bad_address_from_sql() {
325 macro_rules! bad_address_from_sql {
326 ($ty:tt) => {
327 let address: Result<IpNet, _> =
328 FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&[7, PGSQL_AF_INET, 0]));
329 assert_eq!(
330 address.unwrap_err().to_string(),
331 "invalid network address format. input is too short."
332 );
333 };
334 }
335
336 bad_address_from_sql!(Inet);
337 bad_address_from_sql!(Cidr);
338 }
339
340 #[diesel_test_helper::test]
341 fn no_address_from_sql() {
342 macro_rules! test_no_address_from_sql {
343 ($ty:ty) => {
344 let address: Result<IpNet, _> = FromSql::<$ty, Pg>::from_nullable_sql(None);
345 assert_eq!(
346 address.unwrap_err().to_string(),
347 "Unexpected null for non-null column"
348 );
349 };
350 }
351
352 test_no_address_from_sql!(Inet);
353 test_no_address_from_sql!(Cidr);
354 }
355}