1use crate::ast::{
20 CallStyle, Extension, ExtensionFunction, ExtensionOutputValue, ExtensionValue, Literal, Name,
21 RepresentableExtensionValue, Type, Value, ValueKind,
22};
23use crate::entities::SchemaType;
24use crate::evaluator;
25use std::sync::Arc;
26
27#[allow(clippy::expect_used)]
29mod names {
30 use crate::ast::Name;
31 lazy_static::lazy_static! {
32 pub static ref EXTENSION_NAME : Name = Name::parse_unqualified_name("ipaddr").expect("should be a valid identifier");
33 pub static ref IP_FROM_STR_NAME : Name = Name::parse_unqualified_name("ip").expect("should be a valid identifier");
34 pub static ref IS_IPV4 : Name = Name::parse_unqualified_name("isIpv4").expect("should be a valid identifier");
35 pub static ref IS_IPV6 : Name = Name::parse_unqualified_name("isIpv6").expect("should be a valid identifier");
36 pub static ref IS_LOOPBACK : Name = Name::parse_unqualified_name("isLoopback").expect("should be a valid identifier");
37 pub static ref IS_MULTICAST : Name = Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier");
38 pub static ref IS_IN_RANGE : Name = Name::parse_unqualified_name("isInRange").expect("should be a valid identifier");
39 }
40}
41
42const ADVICE_MSG: &str = "maybe you forgot to apply the `ip` constructor?";
45
46const PREFIX_MAX_LEN_V4: u8 = 32;
48const PREFIX_MAX_LEN_V6: u8 = 128;
50const PREFIX_STR_MAX_LEN_V4: u8 = 2;
53const PREFIX_STR_MAX_LEN_V6: u8 = 3;
56const IP_STR_REP_MAX_LEN: u8 = 43;
59
60#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
61struct IPAddr {
62 addr: std::net::IpAddr,
64 prefix: u8,
67}
68
69impl IPAddr {
70 fn typename() -> Name {
72 names::EXTENSION_NAME.clone()
73 }
74
75 fn from_str(str: impl AsRef<str>) -> Result<Self, String> {
86 str.as_ref().parse()
88 }
89
90 fn is_ipv4(&self) -> bool {
92 self.addr.is_ipv4()
93 }
94
95 fn is_ipv6(&self) -> bool {
97 self.addr.is_ipv6()
98 }
99
100 fn is_loopback(&self) -> bool {
102 self.addr.is_loopback() && self.prefix >= if self.is_ipv4() { 8 } else { PREFIX_MAX_LEN_V6 }
106 }
107
108 fn is_multicast(&self) -> bool {
110 self.addr.is_multicast() && self.prefix >= if self.is_ipv4() { 4 } else { 8 }
113 }
114
115 fn is_in_range(&self, other: &Self) -> bool {
117 match (&self.addr, &other.addr) {
118 (std::net::IpAddr::V4(self_v4), std::net::IpAddr::V4(other_v4)) => {
119 let netmask = |prefix: u8| {
120 u32::MAX
121 .checked_shl((PREFIX_MAX_LEN_V4 - prefix).into())
122 .unwrap_or(0)
123 };
124 let hostmask = |prefix: u8| u32::MAX.checked_shr(prefix.into()).unwrap_or(0);
125
126 let self_network = u32::from(*self_v4) & netmask(self.prefix);
127 let other_network = u32::from(*other_v4) & netmask(other.prefix);
128 let self_broadcast = u32::from(*self_v4) | hostmask(self.prefix);
129 let other_broadcast = u32::from(*other_v4) | hostmask(other.prefix);
130 other_network <= self_network && self_broadcast <= other_broadcast
131 }
132 (std::net::IpAddr::V6(self_v6), std::net::IpAddr::V6(other_v6)) => {
133 let netmask = |prefix: u8| {
134 u128::MAX
135 .checked_shl((PREFIX_MAX_LEN_V6 - prefix).into())
136 .unwrap_or(0)
137 };
138 let hostmask = |prefix: u8| u128::MAX.checked_shr(prefix.into()).unwrap_or(0);
139
140 let self_network = u128::from(*self_v6) & netmask(self.prefix);
141 let other_network = u128::from(*other_v6) & netmask(other.prefix);
142 let self_broadcast = u128::from(*self_v6) | hostmask(self.prefix);
143 let other_broadcast = u128::from(*other_v6) | hostmask(other.prefix);
144 other_network <= self_network && self_broadcast <= other_broadcast
145 }
146 (_, _) => false,
147 }
148 }
149}
150
151fn parse_prefix(s: &str, max: u8, max_len: u8) -> Result<u8, String> {
152 if s.len() > max_len as usize {
153 return Err(format!(
154 "error parsing prefix: string length {} is too large",
155 s.len()
156 ));
157 }
158 if s.chars().any(|c| !c.is_ascii_digit()) {
159 return Err(format!("error parsing prefix `{s}`: encountered non-digit"));
160 }
161 if s.starts_with('0') && s != "0" {
162 return Err(format!("error parsing prefix `{s}`: leading zero(s)"));
163 }
164 let res: u8 = s
165 .parse()
166 .map_err(|err| format!("error parsing prefix from the string `{s}`: {err}"))?;
167 if res > max {
168 return Err(format!(
169 "error parsing prefix: {res} is larger than the limit {max}"
170 ));
171 }
172 Ok(res)
173}
174
175impl std::str::FromStr for IPAddr {
176 type Err = String;
177 fn from_str(s: &str) -> Result<Self, Self::Err> {
178 if s.bytes().len() > IP_STR_REP_MAX_LEN as usize {
180 return Err(format!(
181 "error parsing IP address from string `{s}`: string length is too large"
182 ));
183 }
184 str_contains_colons_and_dots(s)?;
186
187 match s.split_once('/') {
189 Some((addr_str, prefix_str)) => {
190 let addr: std::net::IpAddr = addr_str.parse().map_err(|e| {
193 format!("error parsing IP address from the string `{addr_str}`: {e}")
194 })?;
195 let prefix = match addr {
196 std::net::IpAddr::V4(_) => {
197 parse_prefix(prefix_str, PREFIX_MAX_LEN_V4, PREFIX_STR_MAX_LEN_V4)?
198 }
199 std::net::IpAddr::V6(_) => {
200 parse_prefix(prefix_str, PREFIX_MAX_LEN_V6, PREFIX_STR_MAX_LEN_V6)?
201 }
202 };
203 Ok(Self { addr, prefix })
204 }
205 None => match std::net::IpAddr::from_str(s) {
206 Ok(singleaddr) => Ok(Self {
207 addr: singleaddr,
208 prefix: if singleaddr.is_ipv4() {
209 PREFIX_MAX_LEN_V4
210 } else {
211 PREFIX_MAX_LEN_V6
212 },
213 }),
214 Err(_) => Err(format!("invalid IP address: {s}")),
215 },
216 }
217 }
218}
219
220impl std::fmt::Display for IPAddr {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 write!(f, "{}/{}", self.addr, self.prefix)
223 }
224}
225
226impl ExtensionValue for IPAddr {
227 fn typename(&self) -> Name {
228 Self::typename()
229 }
230 fn supports_operator_overloading(&self) -> bool {
231 false
232 }
233}
234
235fn extension_err(msg: impl Into<String>) -> evaluator::EvaluationError {
236 evaluator::EvaluationError::failed_extension_function_application(
237 names::EXTENSION_NAME.clone(),
238 msg.into(),
239 None, None,
241 )
242}
243
244fn contains_at_least_two(s: &str, c: char) -> bool {
246 let idx = s.find(c);
247 match idx {
248 Some(i) => {
249 #[allow(clippy::indexing_slicing)]
264 #[allow(clippy::unwrap_used)]
266 let idx = s.get(i + c.len_utf8()..).unwrap().find(c);
267 idx.is_some()
268 }
269 None => false,
270 }
271}
272
273#[cfg(kani)]
274mod proof {
275
276 #[kani::proof]
278 #[kani::unwind(7)]
279 fn contains_at_least_two_correct() {
280 let buf: [u8; 6] = kani::any();
281 let len: usize = kani::any();
282 kani::assume(len <= 6);
283 let slice = &buf[0..len];
284 if let Ok(s) = std::str::from_utf8(slice) {
285 let pat = kani::any();
286 let _ = super::contains_at_least_two(s, pat);
288 }
289 }
290}
291
292fn str_contains_colons_and_dots(s: &str) -> Result<(), String> {
299 if contains_at_least_two(s, ':') && contains_at_least_two(s, '.') {
300 return Err(format!(
301 "error parsing IP address from string: We do not accept IPv4 embedded in IPv6 (e.g., ::ffff:127.0.0.1). Found: `{}`", &s.to_string()));
302 }
303 Ok(())
304}
305
306fn ip_from_str(arg: &Value) -> evaluator::Result<ExtensionOutputValue> {
309 let str = arg.get_as_string()?;
310 let arg_source_loc = arg.source_loc().cloned();
311 let ipaddr = RepresentableExtensionValue::new(
312 Arc::new(IPAddr::from_str(str.as_str()).map_err(extension_err)?),
313 names::IP_FROM_STR_NAME.clone(),
314 vec![arg.clone().into()],
315 );
316 Ok(Value {
317 value: ValueKind::ExtensionValue(Arc::new(ipaddr)),
318 loc: arg_source_loc, }
320 .into())
321}
322
323fn as_ipaddr(v: &Value) -> Result<&IPAddr, evaluator::EvaluationError> {
324 match &v.value {
325 ValueKind::ExtensionValue(ev) if ev.typename() == IPAddr::typename() => {
326 #[allow(clippy::expect_used)]
328 let ipaddr = ev
329 .value()
330 .as_any()
331 .downcast_ref::<IPAddr>()
332 .expect("already typechecked, so this downcast should succeed");
333 Ok(ipaddr)
334 }
335 ValueKind::Lit(Literal::String(_)) => {
336 Err(evaluator::EvaluationError::type_error_with_advice_single(
337 Type::Extension {
338 name: IPAddr::typename(),
339 },
340 v,
341 ADVICE_MSG.into(),
342 ))
343 }
344 _ => Err(evaluator::EvaluationError::type_error_single(
345 Type::Extension {
346 name: IPAddr::typename(),
347 },
348 v,
349 )),
350 }
351}
352
353fn is_ipv4(arg: &Value) -> evaluator::Result<ExtensionOutputValue> {
356 let ipaddr = as_ipaddr(arg)?;
357 Ok(ipaddr.is_ipv4().into())
358}
359
360fn is_ipv6(arg: &Value) -> evaluator::Result<ExtensionOutputValue> {
363 let ipaddr = as_ipaddr(arg)?;
364 Ok(ipaddr.is_ipv6().into())
365}
366
367fn is_loopback(arg: &Value) -> evaluator::Result<ExtensionOutputValue> {
370 let ipaddr = as_ipaddr(arg)?;
371 Ok(ipaddr.is_loopback().into())
372}
373
374fn is_multicast(arg: &Value) -> evaluator::Result<ExtensionOutputValue> {
377 let ipaddr = as_ipaddr(arg)?;
378 Ok(ipaddr.is_multicast().into())
379}
380
381fn is_in_range(child: &Value, parent: &Value) -> evaluator::Result<ExtensionOutputValue> {
385 let child_ip = as_ipaddr(child)?;
386 let parent_ip = as_ipaddr(parent)?;
387 Ok(child_ip.is_in_range(parent_ip).into())
388}
389
390pub fn extension() -> Extension {
392 let ipaddr_type = SchemaType::Extension {
393 name: IPAddr::typename(),
394 };
395 Extension::new(
396 names::EXTENSION_NAME.clone(),
397 vec![
398 ExtensionFunction::unary(
399 names::IP_FROM_STR_NAME.clone(),
400 CallStyle::FunctionStyle,
401 Box::new(ip_from_str),
402 ipaddr_type.clone(),
403 SchemaType::String,
404 ),
405 ExtensionFunction::unary(
406 names::IS_IPV4.clone(),
407 CallStyle::MethodStyle,
408 Box::new(is_ipv4),
409 SchemaType::Bool,
410 ipaddr_type.clone(),
411 ),
412 ExtensionFunction::unary(
413 names::IS_IPV6.clone(),
414 CallStyle::MethodStyle,
415 Box::new(is_ipv6),
416 SchemaType::Bool,
417 ipaddr_type.clone(),
418 ),
419 ExtensionFunction::unary(
420 names::IS_LOOPBACK.clone(),
421 CallStyle::MethodStyle,
422 Box::new(is_loopback),
423 SchemaType::Bool,
424 ipaddr_type.clone(),
425 ),
426 ExtensionFunction::unary(
427 names::IS_MULTICAST.clone(),
428 CallStyle::MethodStyle,
429 Box::new(is_multicast),
430 SchemaType::Bool,
431 ipaddr_type.clone(),
432 ),
433 ExtensionFunction::binary(
434 names::IS_IN_RANGE.clone(),
435 CallStyle::MethodStyle,
436 Box::new(is_in_range),
437 SchemaType::Bool,
438 (ipaddr_type.clone(), ipaddr_type),
439 ),
440 ],
441 std::iter::empty(),
442 )
443}
444
445#[allow(clippy::panic)]
447#[cfg(test)]
448#[allow(clippy::cognitive_complexity)]
449mod tests {
450 use super::*;
451 use crate::ast::{Expr, Type, Value};
452 use crate::evaluator::test::{basic_entities, basic_request};
453 use crate::evaluator::{evaluation_errors, EvaluationError, Evaluator};
454 use crate::extensions::Extensions;
455 use crate::parser::parse_expr;
456 use cool_asserts::assert_matches;
457 use nonempty::nonempty;
458
459 #[track_caller] fn assert_ipaddr_err<T: std::fmt::Debug>(res: evaluator::Result<T>) {
463 assert_matches!(res, Err(EvaluationError::FailedExtensionFunctionExecution(evaluation_errors::ExtensionFunctionExecutionError { extension_name, .. })) => {
464 assert_eq!(
465 extension_name,
466 Name::parse_unqualified_name("ipaddr")
467 .expect("should be a valid identifier")
468 );
469 });
470 }
471
472 fn ip(arg: impl Into<Literal>) -> Expr {
474 Expr::call_extension_fn(
475 Name::parse_unqualified_name("ip").expect("should be a valid identifier"),
476 vec![Expr::val(arg)],
477 )
478 }
479
480 #[test]
482 fn constructors() {
483 let ext = extension();
484 assert!(ext
485 .get_func(&Name::parse_unqualified_name("ip").expect("should be a valid identifier"))
486 .expect("function should exist")
487 .is_constructor());
488 assert!(!ext
489 .get_func(
490 &Name::parse_unqualified_name("isIpv4").expect("should be a valid identifier")
491 )
492 .expect("function should exist")
493 .is_constructor());
494 assert!(!ext
495 .get_func(
496 &Name::parse_unqualified_name("isIpv6").expect("should be a valid identifier")
497 )
498 .expect("function should exist")
499 .is_constructor());
500 assert!(!ext
501 .get_func(
502 &Name::parse_unqualified_name("isLoopback").expect("should be a valid identifier")
503 )
504 .expect("function should exist")
505 .is_constructor());
506 assert!(!ext
507 .get_func(
508 &Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier")
509 )
510 .expect("function should exist")
511 .is_constructor());
512 assert!(!ext
513 .get_func(
514 &Name::parse_unqualified_name("isInRange").expect("should be a valid identifier")
515 )
516 .expect("function should exist")
517 .is_constructor(),);
518 }
519
520 #[test]
521 fn ip_creation() {
522 let ext_array = [extension()];
523 let exts = Extensions::specific_extensions(&ext_array).unwrap();
524 let request = basic_request();
525 let entities = basic_entities();
526 let eval = Evaluator::new(request, &entities, &exts);
527
528 assert_eq!(
530 eval.interpret_inline_policy(
531 &parse_expr(r#""pancakes" like "pan*""#).expect("parsing error")
532 ),
533 Ok(Value::from(true))
534 );
535
536 assert_eq!(
538 eval.interpret_inline_policy(&Expr::call_extension_fn(
539 Name::parse_unqualified_name("isIpv4").expect("should be a valid identifier"),
540 vec![ip("127.0.0.1")]
541 )),
542 Ok(Value::from(true))
543 );
544 assert_eq!(
545 eval.interpret_inline_policy(&Expr::call_extension_fn(
546 Name::parse_unqualified_name("isIpv6").expect("should be a valid identifier"),
547 vec![ip("127.0.0.1")]
548 )),
549 Ok(Value::from(false))
550 );
551
552 assert_eq!(
554 eval.interpret_inline_policy(&Expr::call_extension_fn(
555 Name::parse_unqualified_name("isIpv4").expect("should be a valid identifier"),
556 vec![ip("::1")]
557 )),
558 Ok(Value::from(false))
559 );
560 assert_eq!(
561 eval.interpret_inline_policy(&Expr::call_extension_fn(
562 Name::parse_unqualified_name("isIpv6").expect("should be a valid identifier"),
563 vec![ip("::1")]
564 )),
565 Ok(Value::from(true))
566 );
567
568 assert_eq!(
570 eval.interpret_inline_policy(&Expr::call_extension_fn(
571 Name::parse_unqualified_name("isIpv4").expect("should be a valid identifier"),
572 vec![ip("::ffff:ff00:1")]
573 )),
574 Ok(Value::from(false))
575 );
576 assert_eq!(
577 eval.interpret_inline_policy(&Expr::call_extension_fn(
578 Name::parse_unqualified_name("isIpv6").expect("should be a valid identifier"),
579 vec![ip("::ffff:ff00:1")]
580 )),
581 Ok(Value::from(true))
582 );
583
584 assert_ipaddr_err(eval.interpret_inline_policy(&ip("380.0.0.1")));
586 assert_ipaddr_err(eval.interpret_inline_policy(&ip("?")));
587 assert_ipaddr_err(eval.interpret_inline_policy(&ip("ab.ab.ab.ab")));
588 assert_ipaddr_err(eval.interpret_inline_policy(&ip("foo::1")));
589 assert_ipaddr_err(eval.interpret_inline_policy(&ip("::ffff:127.0.0.1")));
591 assert_ipaddr_err(eval.interpret_inline_policy(&ip("::127.0.0.1")));
592 assert_matches!(
593 eval.interpret_inline_policy(&Expr::call_extension_fn(
594 Name::parse_unqualified_name("ip").expect("should be a valid identifier"),
595 vec![Expr::set(vec![
596 Expr::val(127),
597 Expr::val(0),
598 Expr::val(0),
599 Expr::val(1)
600 ])]
601 )),
602 Err(EvaluationError::TypeError(evaluation_errors::TypeError { expected, actual, advice, .. })) => {
603 assert_eq!(expected, nonempty![Type::String]);
604 assert_eq!(actual, Type::Set);
605 assert_eq!(advice, None);
606 }
607 );
608
609 assert_matches!(
611 eval.interpret_inline_policy(&Expr::less(ip("127.0.0.1"), ip("10.0.0.10"))),
612 Err(EvaluationError::TypeError(evaluation_errors::TypeError { expected, actual, advice, .. })) => {
613 assert_eq!(expected, nonempty![Type::Long]);
614 assert_eq!(actual, Type::Extension {
615 name: Name::parse_unqualified_name("ipaddr")
616 .expect("should be a valid identifier")
617 });
618 assert_eq!(advice, Some("Only types long support comparison".into()));
619 }
620 );
621 assert_matches!(
623 eval.interpret_inline_policy(&Expr::call_extension_fn(
624 Name::parse_unqualified_name("isIpv4").expect("should be a valid identifier"),
625 vec![Expr::val("127.0.0.1")]
626 )),
627 Err(EvaluationError::TypeError(evaluation_errors::TypeError { expected, actual, advice, .. })) => {
628 assert_eq!(expected, nonempty![Type::Extension {
629 name: Name::parse_unqualified_name("ipaddr")
630 .expect("should be a valid identifier")
631 }]);
632 assert_eq!(actual, Type::String);
633 assert_eq!(advice, Some(ADVICE_MSG.into()));
634 }
635 );
636
637 assert_eq!(
639 eval.interpret_inline_policy(&ip("127.0.0.1"))
640 .unwrap()
641 .to_string(),
642 r#"ip("127.0.0.1")"#
643 );
644 assert_eq!(
645 eval.interpret_inline_policy(&ip("ffee::11"))
646 .unwrap()
647 .to_string(),
648 r#"ip("ffee::11")"#
649 );
650 }
651
652 #[test]
653 fn ip_range_creation() {
654 let ext_array = [extension()];
655 let exts = Extensions::specific_extensions(&ext_array).unwrap();
656 let request = basic_request();
657 let entities = basic_entities();
658 let eval = Evaluator::new(request, &entities, &exts);
659
660 assert_eq!(
662 eval.interpret_inline_policy(&Expr::call_extension_fn(
663 Name::parse_unqualified_name("isIpv4").expect("should be a valid identifier"),
664 vec![ip("127.0.0.1/24")]
665 )),
666 Ok(Value::from(true))
667 );
668 assert_eq!(
669 eval.interpret_inline_policy(&Expr::call_extension_fn(
670 Name::parse_unqualified_name("isIpv6").expect("should be a valid identifier"),
671 vec![ip("127.0.0.1/24")]
672 )),
673 Ok(Value::from(false))
674 );
675
676 assert_eq!(
678 eval.interpret_inline_policy(&Expr::call_extension_fn(
679 Name::parse_unqualified_name("isIpv4").expect("should be a valid identifier"),
680 vec![ip("ffee::/64")]
681 )),
682 Ok(Value::from(false))
683 );
684 assert_eq!(
685 eval.interpret_inline_policy(&Expr::call_extension_fn(
686 Name::parse_unqualified_name("isIpv6").expect("should be a valid identifier"),
687 vec![ip("ffee::/64")]
688 )),
689 Ok(Value::from(true))
690 );
691
692 assert_matches!(eval.interpret_inline_policy(&ip("127.0.0.1/0")), Ok(_));
694 assert_matches!(eval.interpret_inline_policy(&ip("127.0.0.1/32")), Ok(_));
695 assert_matches!(eval.interpret_inline_policy(&ip("ffee::/0")), Ok(_));
696 assert_matches!(eval.interpret_inline_policy(&ip("ffee::/128")), Ok(_));
697
698 assert_ipaddr_err(eval.interpret_inline_policy(&ip("127.0.0.1/8/24")));
700 assert_ipaddr_err(eval.interpret_inline_policy(&ip("fee::/64::1")));
701 assert_ipaddr_err(eval.interpret_inline_policy(&ip("172.0.0.1/64")));
702 assert_ipaddr_err(eval.interpret_inline_policy(&ip("ffee::/132")));
703 assert_ipaddr_err(eval.interpret_inline_policy(&ip("ffee::/+1")));
704 assert_ipaddr_err(eval.interpret_inline_policy(&ip("ffee::/01")));
705 assert_ipaddr_err(eval.interpret_inline_policy(&ip("ffee::/1234")));
706
707 assert_eq!(
709 eval.interpret_inline_policy(&ip("127.0.0.1/0"))
710 .unwrap()
711 .to_string(),
712 r#"ip("127.0.0.1/0")"#
713 );
714 assert_eq!(
715 eval.interpret_inline_policy(&ip("127.0.0.1/8"))
716 .unwrap()
717 .to_string(),
718 r#"ip("127.0.0.1/8")"#
719 );
720 assert_eq!(
721 eval.interpret_inline_policy(&ip("127.0.0.1/32"))
722 .unwrap()
723 .to_string(),
724 r#"ip("127.0.0.1/32")"#
725 );
726 assert_eq!(
727 eval.interpret_inline_policy(&ip("ffee::/64"))
728 .unwrap()
729 .to_string(),
730 r#"ip("ffee::/64")"#
731 );
732 }
733
734 #[test]
735 fn ip_equality() {
736 let ext_array = [extension()];
737 let exts = Extensions::specific_extensions(&ext_array).unwrap();
738 let request = basic_request();
739 let entities = basic_entities();
740 let eval = Evaluator::new(request, &entities, &exts);
741
742 assert_eq!(
744 eval.interpret_inline_policy(&Expr::is_eq(ip("127.0.0.1"), ip("127.0.0.1"))),
745 Ok(Value::from(true))
746 );
747 assert_eq!(
748 eval.interpret_inline_policy(&Expr::is_eq(ip("192.168.0.1"), ip("8.8.8.8"))),
749 Ok(Value::from(false))
750 );
751
752 assert_eq!(
754 eval.interpret_inline_policy(&Expr::is_eq(ip("127.0.0.1"), ip("::1"))),
755 Ok(Value::from(false))
756 );
757 assert_eq!(
758 eval.interpret_inline_policy(&Expr::is_eq(ip("127.0.0.1"), Expr::val("127.0.0.1"))),
759 Ok(Value::from(false))
760 );
761 assert_eq!(
762 eval.interpret_inline_policy(&Expr::is_eq(ip("::1"), Expr::val(1))),
763 Ok(Value::from(false))
764 );
765
766 assert_eq!(
768 eval.interpret_inline_policy(&Expr::is_eq(ip("127.0.0.1"), ip("192.168.0.1/24"))),
769 Ok(Value::from(false))
770 );
771 assert_eq!(
773 eval.interpret_inline_policy(&Expr::is_eq(ip("192.168.0.1/24"), ip("8.8.8.8/8"))),
774 Ok(Value::from(false))
775 );
776 }
777
778 #[test]
779 fn is_loopback_and_is_multicast() {
780 let ext_array = [extension()];
781 let exts = Extensions::specific_extensions(&ext_array).unwrap();
782 let request = basic_request();
783 let entities = basic_entities();
784 let eval = Evaluator::new(request, &entities, &exts);
785
786 assert_eq!(
787 eval.interpret_inline_policy(&Expr::call_extension_fn(
788 Name::parse_unqualified_name("isLoopback").expect("should be a valid identifier"),
789 vec![ip("127.0.0.2")]
790 )),
791 Ok(Value::from(true))
792 );
793 assert_eq!(
794 eval.interpret_inline_policy(&Expr::call_extension_fn(
795 Name::parse_unqualified_name("isLoopback").expect("should be a valid identifier"),
796 vec![ip("::1")]
797 )),
798 Ok(Value::from(true))
799 );
800 assert_eq!(
801 eval.interpret_inline_policy(&Expr::call_extension_fn(
802 Name::parse_unqualified_name("isLoopback").expect("should be a valid identifier"),
803 vec![ip("::2")]
804 )),
805 Ok(Value::from(false))
806 );
807 assert_eq!(
808 eval.interpret_inline_policy(&Expr::call_extension_fn(
809 Name::parse_unqualified_name("isLoopback").expect("should be a valid identifier"),
810 vec![ip("127.255.200.200/0")]
811 )),
812 Ok(Value::from(false))
813 );
814 assert_eq!(
815 eval.interpret_inline_policy(&Expr::call_extension_fn(
816 Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier"),
817 vec![ip("228.228.228.0")]
818 )),
819 Ok(Value::from(true))
820 );
821 assert_eq!(
822 eval.interpret_inline_policy(&Expr::call_extension_fn(
823 Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier"),
824 vec![ip("224.0.0.0/3")]
825 )),
826 Ok(Value::from(false))
827 );
828 assert_eq!(
829 eval.interpret_inline_policy(&Expr::call_extension_fn(
830 Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier"),
831 vec![ip("224.0.0.0/5")]
832 )),
833 Ok(Value::from(true))
834 );
835 assert_eq!(
836 eval.interpret_inline_policy(&Expr::call_extension_fn(
837 Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier"),
838 vec![ip("ff00::/7")]
839 )),
840 Ok(Value::from(false))
841 );
842 assert_eq!(
843 eval.interpret_inline_policy(&Expr::call_extension_fn(
844 Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier"),
845 vec![ip("ff00::/9")]
846 )),
847 Ok(Value::from(true))
848 );
849 assert_eq!(
850 eval.interpret_inline_policy(&Expr::call_extension_fn(
851 Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier"),
852 vec![ip("127.0.0.1")]
853 )),
854 Ok(Value::from(false))
855 );
856 assert_eq!(
857 eval.interpret_inline_policy(&Expr::call_extension_fn(
858 Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier"),
859 vec![ip("127.0.0.1/1")]
860 )),
861 Ok(Value::from(false))
862 );
863 assert_eq!(
864 eval.interpret_inline_policy(&Expr::call_extension_fn(
865 Name::parse_unqualified_name("isMulticast").expect("should be a valid identifier"),
866 vec![ip("ff00::2")]
867 )),
868 Ok(Value::from(true))
869 );
870 }
871
872 #[test]
873 fn ip_is_in_range() {
874 let ext_array = [extension()];
875 let exts = Extensions::specific_extensions(&ext_array).unwrap();
876 let request = basic_request();
877 let entities = basic_entities();
878 let eval = Evaluator::new(request, &entities, &exts);
879
880 assert_eq!(
881 eval.interpret_inline_policy(&Expr::call_extension_fn(
882 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
883 vec![ip("192.168.0.1/24"), ip("192.168.0.1/24")]
884 )),
885 Ok(Value::from(true))
886 );
887 assert_eq!(
888 eval.interpret_inline_policy(&Expr::call_extension_fn(
889 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
890 vec![ip("192.168.0.1"), ip("192.168.0.1/28")]
891 )),
892 Ok(Value::from(true))
893 );
894 assert_eq!(
895 eval.interpret_inline_policy(&Expr::call_extension_fn(
896 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
897 vec![ip("192.168.0.10"), ip("192.168.0.1/24")]
898 )),
899 Ok(Value::from(true))
900 );
901 assert_eq!(
902 eval.interpret_inline_policy(&Expr::call_extension_fn(
903 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
904 vec![ip("192.168.0.10"), ip("192.168.0.1/28")]
905 )),
906 Ok(Value::from(true))
907 );
908 assert_eq!(
909 eval.interpret_inline_policy(&Expr::call_extension_fn(
910 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
911 vec![ip("192.168.0.75"), ip("192.168.0.1/24")]
912 )),
913 Ok(Value::from(true))
914 );
915 assert_eq!(
916 eval.interpret_inline_policy(&Expr::call_extension_fn(
917 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
918 vec![ip("192.168.0.75"), ip("192.168.0.1/28")]
919 )),
920 Ok(Value::from(false))
921 );
922 assert_eq!(
924 eval.interpret_inline_policy(&Expr::call_extension_fn(
925 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
926 vec![ip("192.168.0.1"), ip("192.168.0.1")]
927 )),
928 Ok(Value::from(true))
929 );
930
931 assert_eq!(
932 eval.interpret_inline_policy(&Expr::call_extension_fn(
933 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
934 vec![ip("1:2:3:4::"), ip("1:2:3:4::/48")]
935 )),
936 Ok(Value::from(true))
937 );
938 assert_eq!(
939 eval.interpret_inline_policy(&Expr::call_extension_fn(
940 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
941 vec![ip("1:2:3:4::"), ip("1:2:3:4::/52")]
942 )),
943 Ok(Value::from(true))
944 );
945 assert_eq!(
946 eval.interpret_inline_policy(&Expr::call_extension_fn(
947 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
948 vec![ip("1:2:3:6::"), ip("1:2:3:4::/48")]
949 )),
950 Ok(Value::from(true))
951 );
952 assert_eq!(
953 eval.interpret_inline_policy(&Expr::call_extension_fn(
954 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
955 vec![ip("1:2:3:6::"), ip("1:2:3:4::/52")]
956 )),
957 Ok(Value::from(true))
958 );
959 assert_eq!(
960 eval.interpret_inline_policy(&Expr::call_extension_fn(
961 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
962 vec![ip("1:2:3:ffff::"), ip("1:2:3:4::/48")]
963 )),
964 Ok(Value::from(true))
965 );
966 assert_eq!(
967 eval.interpret_inline_policy(&Expr::call_extension_fn(
968 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
969 vec![ip("1:2:3:ffff::"), ip("1:2:3:4::/52")]
970 )),
971 Ok(Value::from(false))
972 );
973 assert_eq!(
975 eval.interpret_inline_policy(&Expr::call_extension_fn(
976 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
977 vec![ip("1:2:3:4::"), ip("1:2:3:4::")]
978 )),
979 Ok(Value::from(true))
980 );
981
982 assert_eq!(
984 eval.interpret_inline_policy(&Expr::call_extension_fn(
985 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
986 vec![ip("192.168.0.1"), ip("1:2:3:4::/48")]
987 )),
988 Ok(Value::from(false))
989 );
990 }
991
992 #[test]
993 fn more_ip_semantics() {
994 let ext_array = [extension()];
995 let exts = Extensions::specific_extensions(&ext_array).unwrap();
996 let request = basic_request();
997 let entities = basic_entities();
998 let eval = Evaluator::new(request, &entities, &exts);
999
1000 assert_eq!(
1001 eval.interpret_inline_policy(&Expr::is_eq(ip("10.0.0.0"), ip("10.0.0.0"))),
1002 Ok(Value::from(true))
1003 );
1004 assert_eq!(
1005 eval.interpret_inline_policy(&Expr::is_eq(ip("10.0.0.0"), ip("10.0.0.1"))),
1006 Ok(Value::from(false))
1007 );
1008 assert_eq!(
1009 eval.interpret_inline_policy(&Expr::is_eq(ip("10.0.0.0/32"), ip("10.0.0.0"))),
1010 Ok(Value::from(true))
1011 );
1012 assert_eq!(
1013 eval.interpret_inline_policy(&Expr::is_eq(ip("10.0.0.0/24"), ip("10.0.0.0"))),
1014 Ok(Value::from(false))
1015 );
1016 assert_eq!(
1017 eval.interpret_inline_policy(&Expr::is_eq(ip("10.0.0.0/32"), ip("10.0.0.0/32"))),
1018 Ok(Value::from(true))
1019 );
1020 assert_eq!(
1021 eval.interpret_inline_policy(&Expr::is_eq(ip("10.0.0.0/24"), ip("10.0.0.0/32"))),
1022 Ok(Value::from(false))
1023 );
1024 assert_eq!(
1025 eval.interpret_inline_policy(&Expr::is_eq(ip("10.0.0.0/24"), ip("10.0.0.1/24"))),
1026 Ok(Value::from(false))
1027 );
1028 assert_eq!(
1029 eval.interpret_inline_policy(&Expr::is_eq(ip("10.0.0.1/24"), ip("10.0.0.1/29"))),
1030 Ok(Value::from(false))
1031 );
1032 assert_eq!(
1033 eval.interpret_inline_policy(&Expr::call_extension_fn(
1034 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1035 vec![ip("10.0.0.0"), ip("10.0.0.0/24")]
1036 )),
1037 Ok(Value::from(true))
1038 );
1039 assert_eq!(
1040 eval.interpret_inline_policy(&Expr::call_extension_fn(
1041 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1042 vec![ip("10.0.0.0"), ip("10.0.0.0/32")]
1043 )),
1044 Ok(Value::from(true))
1045 );
1046 assert_eq!(
1047 eval.interpret_inline_policy(&Expr::call_extension_fn(
1048 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1049 vec![ip("10.0.0.0"), ip("10.0.0.1/24")]
1050 )),
1051 Ok(Value::from(true))
1052 );
1053 assert_eq!(
1054 eval.interpret_inline_policy(&Expr::call_extension_fn(
1055 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1056 vec![ip("10.0.0.0"), ip("10.0.0.1/32")]
1057 )),
1058 Ok(Value::from(false))
1059 );
1060 assert_eq!(
1061 eval.interpret_inline_policy(&Expr::call_extension_fn(
1062 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1063 vec![ip("10.0.0.1"), ip("10.0.0.0/24")]
1064 )),
1065 Ok(Value::from(true))
1066 );
1067 assert_eq!(
1068 eval.interpret_inline_policy(&Expr::call_extension_fn(
1069 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1070 vec![ip("10.0.0.1"), ip("10.0.0.1/24")]
1071 )),
1072 Ok(Value::from(true))
1073 );
1074 assert_eq!(
1075 eval.interpret_inline_policy(&Expr::call_extension_fn(
1076 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1077 vec![ip("10.0.0.0/24"), ip("10.0.0.0/32")]
1078 )),
1079 Ok(Value::from(false))
1080 );
1081 assert_eq!(
1082 eval.interpret_inline_policy(&Expr::call_extension_fn(
1083 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1084 vec![ip("10.0.0.0/32"), ip("10.0.0.0/24")]
1085 )),
1086 Ok(Value::from(true))
1087 );
1088 assert_eq!(
1089 eval.interpret_inline_policy(&Expr::call_extension_fn(
1090 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1091 vec![ip("10.0.0.1/24"), ip("10.0.0.0/24")]
1092 )),
1093 Ok(Value::from(true))
1094 );
1095 assert_eq!(
1096 eval.interpret_inline_policy(&Expr::call_extension_fn(
1097 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1098 vec![ip("10.0.0.1/24"), ip("10.0.0.1/24")]
1099 )),
1100 Ok(Value::from(true))
1101 );
1102 assert_eq!(
1103 eval.interpret_inline_policy(&Expr::call_extension_fn(
1104 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1105 vec![ip("10.0.0.0/24"), ip("10.0.0.1/24")]
1106 )),
1107 Ok(Value::from(true))
1108 );
1109 assert_eq!(
1110 eval.interpret_inline_policy(&Expr::call_extension_fn(
1111 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1112 vec![ip("10.0.0.0/24"), ip("10.0.0.0/29")]
1113 )),
1114 Ok(Value::from(false))
1115 );
1116 assert_eq!(
1117 eval.interpret_inline_policy(&Expr::call_extension_fn(
1118 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1119 vec![ip("10.0.0.0/29"), ip("10.0.0.0/24")]
1120 )),
1121 Ok(Value::from(true))
1122 );
1123 assert_eq!(
1124 eval.interpret_inline_policy(&Expr::call_extension_fn(
1125 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1126 vec![ip("10.0.0.0/24"), ip("10.0.0.1/29")]
1127 )),
1128 Ok(Value::from(false))
1129 );
1130 assert_eq!(
1131 eval.interpret_inline_policy(&Expr::call_extension_fn(
1132 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1133 vec![ip("10.0.0.0/29"), ip("10.0.0.1/24")]
1134 )),
1135 Ok(Value::from(true))
1136 );
1137 assert_eq!(
1138 eval.interpret_inline_policy(&Expr::call_extension_fn(
1139 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1140 vec![ip("10.0.0.1/24"), ip("10.0.0.0/29")]
1141 )),
1142 Ok(Value::from(false))
1143 );
1144 assert_eq!(
1145 eval.interpret_inline_policy(&Expr::call_extension_fn(
1146 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1147 vec![ip("10.0.0.1/29"), ip("10.0.0.0/24")]
1148 )),
1149 Ok(Value::from(true))
1150 );
1151 assert_eq!(
1152 eval.interpret_inline_policy(&Expr::call_extension_fn(
1153 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1154 vec![ip("10.0.0.0/32"), ip("10.0.0.0/32")]
1155 )),
1156 Ok(Value::from(true))
1157 );
1158 assert_eq!(
1159 eval.interpret_inline_policy(&Expr::call_extension_fn(
1160 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1161 vec![ip("10.0.0.0/32"), ip("10.0.0.0")]
1162 )),
1163 Ok(Value::from(true))
1164 );
1165 assert_ipaddr_err(eval.interpret_inline_policy(&Expr::call_extension_fn(
1166 Name::parse_unqualified_name("isInRange").expect("should be a valid identifier"),
1167 vec![ip("10.0.0.0/33"), ip("10.0.0.0/32")],
1168 )));
1169 }
1170
1171 #[test]
1172 fn test_contains_at_least_two() {
1173 assert!(contains_at_least_two(":::", ':'));
1174 assert!(contains_at_least_two("::", ':'));
1175 assert!(!contains_at_least_two(":", ':'));
1176 }
1177
1178 #[test]
1179 fn test_contains_two_multibyte() {
1180 assert!(!contains_at_least_two("\u{f1b}", '\u{f1b}'));
1181 }
1182}