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