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(const _: () =
{
use diesel;
impl<'__expr> diesel::expression::AsExpression<Inet> for
&'__expr IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<Inet, Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<Inet>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr>
diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>
for &'__expr IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Inet>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr, '__expr2> diesel::expression::AsExpression<Inet> for
&'__expr2 &'__expr IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<Inet, Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<Inet>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr, '__expr2>
diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>
for &'__expr2 &'__expr IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Inet>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl<__DB>
diesel::serialize::ToSql<diesel::sql_types::Nullable<Inet>, __DB>
for IpNet where __DB: diesel::backend::Backend,
Self: diesel::serialize::ToSql<Inet, __DB> {
fn to_sql<'__b>(&'__b self,
out: &mut diesel::serialize::Output<'__b, '_, __DB>)
-> diesel::serialize::Result {
diesel::serialize::ToSql::<Inet, __DB>::to_sql(self, out)
}
}
impl diesel::expression::AsExpression<Inet> for IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<Inet, Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<Inet>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>
for IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Inet>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl<'__expr> diesel::expression::AsExpression<Cidr> for
&'__expr IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<Cidr, Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<Cidr>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr>
diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>
for &'__expr IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Cidr>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr, '__expr2> diesel::expression::AsExpression<Cidr> for
&'__expr2 &'__expr IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<Cidr, Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<Cidr>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr, '__expr2>
diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>
for &'__expr2 &'__expr IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Cidr>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl<__DB>
diesel::serialize::ToSql<diesel::sql_types::Nullable<Cidr>, __DB>
for IpNet where __DB: diesel::backend::Backend,
Self: diesel::serialize::ToSql<Cidr, __DB> {
fn to_sql<'__b>(&'__b self,
out: &mut diesel::serialize::Output<'__b, '_, __DB>)
-> diesel::serialize::Result {
diesel::serialize::ToSql::<Cidr, __DB>::to_sql(self, out)
}
}
impl diesel::expression::AsExpression<Cidr> for IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<Cidr, Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<Cidr>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>
for IpNet {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Cidr>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
};AsExpression, const _: () =
{
use diesel;
impl<__DB, __ST> diesel::deserialize::Queryable<__ST, __DB> for IpNet
where __DB: diesel::backend::Backend,
__ST: diesel::sql_types::SingleValue,
Self: diesel::deserialize::FromSql<__ST, __DB> {
type Row = Self;
fn build(row: Self) -> diesel::deserialize::Result<Self> {
diesel::deserialize::Result::Ok(row)
}
}
};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 FromSql<Inet, Pg> for IpNet {
fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
let bytes = value.as_bytes();
if !(4 <= bytes.len()) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid network address format. {0}",
"input is too short."))
}).into());
};
let af = bytes[0];
let prefix = bytes[1];
let net_type = bytes[2];
let len = bytes[3];
if !(net_type == 0) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid network address format. {0}",
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("returned type isn\'t a {0}",
"Inet"))
})))
}).into());
};
if af == PGSQL_AF_INET {
if !(bytes.len() == 8) {
return Err("invalid network address format".into());
};
if !(len == 4) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid network address format. {0}",
"the data isn't the size of ipv4"))
}).into());
};
let b = &bytes[4..];
let addr = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
let inet = Ipv4Net::new(addr, prefix)?;
Ok(IpNet::V4(inet))
} else if af == PGSQL_AF_INET6 {
if !(bytes.len() == 20) {
return Err("invalid network address format".into());
};
if !(len == 16) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid network address format. {0}",
"the data isn't the size of ipv6"))
}).into());
};
let b = &bytes[4..];
let addr =
Ipv6Addr::from([b[0], b[1], b[2], b[3], b[4], b[5], b[6],
b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14],
b[15]]);
let inet = Ipv6Net::new(addr, prefix)?;
Ok(IpNet::V6(inet))
} else { Err("invalid network address format".into()) }
}
}
impl ToSql<Inet, Pg> for IpNet {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
-> serialize::Result {
let net_type = 0;
match *self {
IpNet::V4(ref net) => {
let mut data = [0u8; 8];
let af = PGSQL_AF_INET;
let prefix = net.prefix_len();
let len: u8 = 4;
let addr =
if (net_type == 0) {
net.addr().octets()
} else { net.network().octets() };
data[0] = af;
data[1] = prefix;
data[2] = net_type;
data[3] = len;
data[4..].copy_from_slice(&addr);
out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
}
IpNet::V6(ref net) => {
let mut data = [0u8; 20];
let af = PGSQL_AF_INET6;
let prefix = net.prefix_len();
let len: u8 = 16;
let addr =
if (net_type == 0) {
net.addr().octets()
} else { net.network().octets() };
data[0] = af;
data[1] = prefix;
data[2] = net_type;
data[3] = len;
data[4..].copy_from_slice(&addr);
out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
}
}
}
}impl_Sql!(Inet, 0);
147impl FromSql<Cidr, Pg> for IpNet {
fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
let bytes = value.as_bytes();
if !(4 <= bytes.len()) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid network address format. {0}",
"input is too short."))
}).into());
};
let af = bytes[0];
let prefix = bytes[1];
let net_type = bytes[2];
let len = bytes[3];
if !(net_type == 1) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid network address format. {0}",
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("returned type isn\'t a {0}",
"Cidr"))
})))
}).into());
};
if af == PGSQL_AF_INET {
if !(bytes.len() == 8) {
return Err("invalid network address format".into());
};
if !(len == 4) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid network address format. {0}",
"the data isn't the size of ipv4"))
}).into());
};
let b = &bytes[4..];
let addr = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
let inet = Ipv4Net::new(addr, prefix)?;
Ok(IpNet::V4(inet))
} else if af == PGSQL_AF_INET6 {
if !(bytes.len() == 20) {
return Err("invalid network address format".into());
};
if !(len == 16) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid network address format. {0}",
"the data isn't the size of ipv6"))
}).into());
};
let b = &bytes[4..];
let addr =
Ipv6Addr::from([b[0], b[1], b[2], b[3], b[4], b[5], b[6],
b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14],
b[15]]);
let inet = Ipv6Net::new(addr, prefix)?;
Ok(IpNet::V6(inet))
} else { Err("invalid network address format".into()) }
}
}
impl ToSql<Cidr, Pg> for IpNet {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
-> serialize::Result {
let net_type = 1;
match *self {
IpNet::V4(ref net) => {
let mut data = [0u8; 8];
let af = PGSQL_AF_INET;
let prefix = net.prefix_len();
let len: u8 = 4;
let addr =
if (net_type == 0) {
net.addr().octets()
} else { net.network().octets() };
data[0] = af;
data[1] = prefix;
data[2] = net_type;
data[3] = len;
data[4..].copy_from_slice(&addr);
out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
}
IpNet::V6(ref net) => {
let mut data = [0u8; 20];
let af = PGSQL_AF_INET6;
let prefix = net.prefix_len();
let len: u8 = 16;
let addr =
if (net_type == 0) {
net.addr().octets()
} else { net.network().octets() };
data[0] = af;
data[1] = prefix;
data[2] = net_type;
data[3] = len;
data[4..].copy_from_slice(&addr);
out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
}
}
}
}impl_Sql!(Cidr, 1);
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use crate::test_helpers::format_error;
153
154 #[diesel_test_helper::test]
155 fn v4address_to_sql() {
156 macro_rules! test_to_sql {
157 ($ty:ty, $net_type:expr) => {
158 let mut buffer = Vec::new();
159 {
160 let mut bytes = Output::test(ByteWrapper(&mut buffer));
161 let test_address =
162 IpNet::V4(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
163 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
164 }
165 assert_eq!(buffer, vec![PGSQL_AF_INET, 32, $net_type, 4, 127, 0, 0, 1]);
166 };
167 }
168
169 test_to_sql!(Inet, 0);
170 test_to_sql!(Cidr, 1);
171 }
172
173 #[diesel_test_helper::test]
174 fn v4_masked_address_to_sql() {
175 macro_rules! test_to_sql {
176 ($ty:ty, $net_type:expr, $last_byte:expr) => {
177 let mut buffer = Vec::new();
178 {
179 let mut bytes = Output::test(ByteWrapper(&mut buffer));
180 let test_address =
181 IpNet::V4(Ipv4Net::new(Ipv4Addr::new(192, 168, 0, 1), 24).unwrap());
182 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
183 }
184 assert_eq!(
185 buffer,
186 vec![PGSQL_AF_INET, 24, $net_type, 4, 192, 168, 0, $last_byte]
187 );
188 };
189 }
190
191 test_to_sql!(Inet, 0, 1);
192 test_to_sql!(Cidr, 1, 0);
193 }
194
195 #[diesel_test_helper::test]
196 fn some_v4address_from_sql() {
197 macro_rules! test_some_address_from_sql {
198 ($ty:tt) => {
199 let input_address =
200 IpNet::V4(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
201 let mut buffer = Vec::new();
202 {
203 let mut bytes = Output::test(ByteWrapper(&mut buffer));
204 ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
205 }
206 let output_address =
207 FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
208 assert_eq!(input_address, output_address);
209 };
210 }
211
212 test_some_address_from_sql!(Cidr);
213 test_some_address_from_sql!(Inet);
214 }
215
216 #[diesel_test_helper::test]
217 fn v6address_to_sql() {
218 macro_rules! test_to_sql {
219 ($ty:ty, $net_type:expr) => {
220 let mut buffer = Vec::new();
221 {
222 let mut bytes = Output::test(ByteWrapper(&mut buffer));
223 let test_address = IpNet::V6(
224 Ipv6Net::new(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 0), 64).unwrap(),
225 );
226 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
227 }
228 assert_eq!(
229 buffer,
230 vec![
231 PGSQL_AF_INET6,
232 64,
233 $net_type,
234 16,
235 0,
236 0xfd,
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 0,
250 0,
251 ]
252 );
253 };
254 }
255
256 test_to_sql!(Inet, 0);
257 test_to_sql!(Cidr, 1);
258 }
259
260 #[diesel_test_helper::test]
261 fn v6_masked_address_from_sql() {
262 macro_rules! test_to_sql {
263 ($ty:ty, $net_type:expr, $last_byte:expr) => {
264 let mut buffer = Vec::new();
265 {
266 let mut bytes = Output::test(ByteWrapper(&mut buffer));
267 let test_address = IpNet::V6(
268 Ipv6Net::new(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 1), 64).unwrap(),
269 );
270 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
271 }
272 assert_eq!(
273 buffer,
274 vec![
275 PGSQL_AF_INET6,
276 64,
277 $net_type,
278 16,
279 0,
280 0xfd,
281 0,
282 0,
283 0,
284 0,
285 0,
286 0,
287 0,
288 0,
289 0,
290 0,
291 0,
292 0,
293 0,
294 $last_byte,
295 ]
296 );
297 };
298 }
299
300 test_to_sql!(Inet, 0, 1);
301 test_to_sql!(Cidr, 1, 0);
302 }
303
304 #[diesel_test_helper::test]
305 fn some_v6address_from_sql() {
306 macro_rules! test_some_address_from_sql {
307 ($ty:tt) => {
308 let input_address =
309 IpNet::V6(Ipv6Net::new(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 0), 64).unwrap());
310 let mut buffer = Vec::new();
311 {
312 let mut bytes = Output::test(ByteWrapper(&mut buffer));
313 ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
314 }
315 let output_address =
316 FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
317 assert_eq!(input_address, output_address);
318 };
319 }
320
321 test_some_address_from_sql!(Inet);
322 test_some_address_from_sql!(Cidr);
323 }
324
325 #[diesel_test_helper::test]
326 fn bad_address_from_sql() {
327 macro_rules! bad_address_from_sql {
328 ($ty:tt) => {
329 let address: Result<IpNet, _> =
330 FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&[7, PGSQL_AF_INET, 0]));
331 assert_eq!(
332 format_error(&*address.unwrap_err()),
333 "invalid network address format. input is too short."
334 );
335 };
336 }
337
338 bad_address_from_sql!(Inet);
339 bad_address_from_sql!(Cidr);
340 }
341
342 #[diesel_test_helper::test]
343 fn no_address_from_sql() {
344 macro_rules! test_no_address_from_sql {
345 ($ty:ty) => {
346 let address: Result<IpNet, _> = FromSql::<$ty, Pg>::from_nullable_sql(None);
347 assert_eq!(
348 format_error(&*address.unwrap_err()),
349 "Unexpected null for non-null column"
350 );
351 };
352 }
353
354 test_no_address_from_sql!(Inet);
355 test_no_address_from_sql!(Cidr);
356 }
357}