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