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(const _: () =
{
use diesel;
impl<'__expr> diesel::expression::AsExpression<Inet> for
&'__expr IpNetwork {
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 IpNetwork {
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 IpNetwork {
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 IpNetwork {
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 IpNetwork 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 IpNetwork {
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 IpNetwork {
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 IpNetwork {
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 IpNetwork {
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 IpNetwork {
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 IpNetwork {
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 IpNetwork 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 IpNetwork {
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 IpNetwork {
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
IpNetwork 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)]
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 FromSql<Inet, Pg> for IpNetwork {
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 = Ipv4Network::new(addr, prefix)?;
Ok(IpNetwork::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 = Ipv6Network::new(addr, prefix)?;
Ok(IpNetwork::V6(inet))
} else { Err("invalid network address format".into()) }
}
}
impl ToSql<Inet, Pg> for IpNetwork {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
-> serialize::Result {
use self::ipnetwork::IpNetwork::*;
let net_type = 0;
match *self {
V4(ref net) => {
let mut data = [0u8; 8];
let af = PGSQL_AF_INET;
let prefix = net.prefix();
let len: u8 = 4;
let addr = net.ip().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)
}
V6(ref net) => {
let mut data = [0u8; 20];
let af = PGSQL_AF_INET6;
let prefix = net.prefix();
let len: u8 = 16;
let addr = net.ip().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);
141impl FromSql<Cidr, Pg> for IpNetwork {
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 = Ipv4Network::new(addr, prefix)?;
Ok(IpNetwork::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 = Ipv6Network::new(addr, prefix)?;
Ok(IpNetwork::V6(inet))
} else { Err("invalid network address format".into()) }
}
}
impl ToSql<Cidr, Pg> for IpNetwork {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
-> serialize::Result {
use self::ipnetwork::IpNetwork::*;
let net_type = 1;
match *self {
V4(ref net) => {
let mut data = [0u8; 8];
let af = PGSQL_AF_INET;
let prefix = net.prefix();
let len: u8 = 4;
let addr = net.ip().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)
}
V6(ref net) => {
let mut data = [0u8; 20];
let af = PGSQL_AF_INET6;
let prefix = net.prefix();
let len: u8 = 16;
let addr = net.ip().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);
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use crate::test_helpers::format_error;
147
148 #[diesel_test_helper::test]
149 fn v4address_to_sql() {
150 macro_rules! test_to_sql {
151 ($ty:ty, $net_type:expr) => {
152 let mut buffer = Vec::new();
153 {
154 let mut bytes = Output::test(ByteWrapper(&mut buffer));
155 let test_address =
156 IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
157 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
158 }
159 assert_eq!(buffer, vec![PGSQL_AF_INET, 32, $net_type, 4, 127, 0, 0, 1]);
160 };
161 }
162
163 test_to_sql!(Inet, 0);
164 test_to_sql!(Cidr, 1);
165 }
166
167 #[diesel_test_helper::test]
168 fn some_v4address_from_sql() {
169 macro_rules! test_some_address_from_sql {
170 ($ty:tt) => {
171 let input_address =
172 IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
173 let mut buffer = Vec::new();
174 {
175 let mut bytes = Output::test(ByteWrapper(&mut buffer));
176 ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
177 }
178 let output_address =
179 FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
180 assert_eq!(input_address, output_address);
181 };
182 }
183
184 test_some_address_from_sql!(Cidr);
185 test_some_address_from_sql!(Inet);
186 }
187
188 #[diesel_test_helper::test]
189 fn v6address_to_sql() {
190 macro_rules! test_to_sql {
191 ($ty:ty, $net_type:expr) => {
192 let mut buffer = Vec::new();
193 {
194 let mut bytes = Output::test(ByteWrapper(&mut buffer));
195 let test_address = IpNetwork::V6(
196 Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 64).unwrap(),
197 );
198 ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
199 }
200 assert_eq!(
201 buffer,
202 vec![
203 PGSQL_AF_INET6,
204 64,
205 $net_type,
206 16,
207 0,
208 0,
209 0,
210 0,
211 0,
212 0,
213 0,
214 0,
215 0,
216 0,
217 0,
218 0,
219 0,
220 0,
221 0,
222 1,
223 ]
224 );
225 };
226 }
227
228 test_to_sql!(Inet, 0);
229 test_to_sql!(Cidr, 1);
230 }
231
232 #[diesel_test_helper::test]
233 fn some_v6address_from_sql() {
234 macro_rules! test_some_address_from_sql {
235 ($ty:tt) => {
236 let input_address = IpNetwork::V6(
237 Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 64).unwrap(),
238 );
239 let mut buffer = Vec::new();
240 {
241 let mut bytes = Output::test(ByteWrapper(&mut buffer));
242 ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
243 }
244 let output_address =
245 FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
246 assert_eq!(input_address, output_address);
247 };
248 }
249
250 test_some_address_from_sql!(Inet);
251 test_some_address_from_sql!(Cidr);
252 }
253
254 #[diesel_test_helper::test]
255 fn bad_address_from_sql() {
256 macro_rules! bad_address_from_sql {
257 ($ty:tt) => {
258 let address: Result<IpNetwork, _> =
259 FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&[7, PGSQL_AF_INET, 0]));
260 assert_eq!(
261 format_error(&*address.unwrap_err()),
262 "invalid network address format. input is too short."
263 );
264 };
265 }
266
267 bad_address_from_sql!(Inet);
268 bad_address_from_sql!(Cidr);
269 }
270
271 #[diesel_test_helper::test]
272 fn no_address_from_sql() {
273 macro_rules! test_no_address_from_sql {
274 ($ty:ty) => {
275 let address: Result<IpNetwork, _> = FromSql::<$ty, Pg>::from_nullable_sql(None);
276 assert_eq!(
277 format_error(&*address.unwrap_err()),
278 "Unexpected null for non-null column"
279 );
280 };
281 }
282
283 test_no_address_from_sql!(Inet);
284 test_no_address_from_sql!(Cidr);
285 }
286}