Skip to content

Commit 04504ae

Browse files
authored
fix: net.parse_cidr support IPv6 (#1909)
Signed-off-by: John Gardiner Myers <[email protected]>
1 parent 24686d5 commit 04504ae

File tree

4 files changed

+138
-14
lines changed

4 files changed

+138
-14
lines changed

kclvm/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

kclvm/runtime/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ lazy_static = "1.4.0"
1212
generational-arena = "0.2.9"
1313
base64 = "0.13.0"
1414
base32 = "0.4.0"
15+
cidr = "0.3.1"
1516
libc = "0.2.112"
1617
itertools = "0.10.3"
1718
unic-ucd-bidi = "0.9"

kclvm/runtime/src/net/mod.rs

Lines changed: 129 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Copyright The KCL Authors. All rights reserved.
22
3+
use cidr::IpCidr;
34
use std::net::IpAddr;
45
use std::net::Ipv4Addr;
56
use std::net::Ipv6Addr;
@@ -475,19 +476,14 @@ pub extern "C-unwind" fn kclvm_net_parse_CIDR(
475476
let kwargs = ptr_as_ref(kwargs);
476477
let ctx = mut_ptr_as_ref(ctx);
477478

478-
if let Some(cidr) = get_call_arg_str(args, kwargs, 0, Some("cidr")) {
479-
let parts: Vec<&str> = cidr.split('/').collect();
480-
if parts.len() == 2 {
481-
let ip = parts[0];
482-
let mask = parts[1];
483-
if let Ok(ip) = Ipv4Addr::from_str(ip) {
484-
if let Ok(mask) = mask.parse::<u8>() {
485-
let ip_value = ValueRef::str(ip.to_string().as_str());
486-
let mask_value = ValueRef::int(mask as i64);
487-
return ValueRef::dict(Some(&[("ip", &ip_value), ("mask", &mask_value)]))
488-
.into_raw(ctx);
489-
}
490-
}
479+
if let Some(cidr) = get_call_arg(args, kwargs, 0, Some("cidr")) {
480+
if cidr.is_none() {
481+
return ValueRef::none().into_raw(ctx);
482+
}
483+
if let Ok(cidr) = IpCidr::from_str(&cidr.as_str()) {
484+
let ip = ValueRef::str(&cidr.first_address().to_string());
485+
let mask = ValueRef::int(cidr.network_length().into());
486+
return ValueRef::dict(Some(&[("ip", &ip), ("mask", &mask)])).into_raw(ctx);
491487
}
492488
return ValueRef::dict(None).into_raw(ctx);
493489
}
@@ -934,4 +930,124 @@ mod test_net {
934930
);
935931
std::panic::set_hook(prev_hook);
936932
}
933+
934+
#[test]
935+
#[allow(non_snake_case)]
936+
fn test_parse_CIDR() {
937+
let cases = [
938+
(ValueRef::none(), ValueRef::none()),
939+
(
940+
ValueRef::str("0.0.0.0/0"),
941+
ValueRef::dict(Some(&[
942+
("ip", &ValueRef::str("0.0.0.0")),
943+
("mask", &ValueRef::int(0)),
944+
])),
945+
),
946+
(
947+
ValueRef::str("::/0"),
948+
ValueRef::dict(Some(&[
949+
("ip", &ValueRef::str("::")),
950+
("mask", &ValueRef::int(0)),
951+
])),
952+
),
953+
(
954+
ValueRef::str("10.0.0.0/8"),
955+
ValueRef::dict(Some(&[
956+
("ip", &ValueRef::str("10.0.0.0")),
957+
("mask", &ValueRef::int(8)),
958+
])),
959+
),
960+
(
961+
ValueRef::str("2001:db8::/56"),
962+
ValueRef::dict(Some(&[
963+
("ip", &ValueRef::str("2001:db8::")),
964+
("mask", &ValueRef::int(56)),
965+
])),
966+
),
967+
(
968+
ValueRef::str("10.1.2.3/32"),
969+
ValueRef::dict(Some(&[
970+
("ip", &ValueRef::str("10.1.2.3")),
971+
("mask", &ValueRef::int(32)),
972+
])),
973+
),
974+
(
975+
ValueRef::str("2001:db8:1:2:3:4:5:7/128"),
976+
ValueRef::dict(Some(&[
977+
("ip", &ValueRef::str("2001:db8:1:2:3:4:5:7")),
978+
("mask", &ValueRef::int(128)),
979+
])),
980+
),
981+
(
982+
ValueRef::str("10.1.2.3"),
983+
ValueRef::dict(Some(&[
984+
("ip", &ValueRef::str("10.1.2.3")),
985+
("mask", &ValueRef::int(32)),
986+
])),
987+
),
988+
(
989+
ValueRef::str("2001:db8:1:2:3:4:5:7"),
990+
ValueRef::dict(Some(&[
991+
("ip", &ValueRef::str("2001:db8:1:2:3:4:5:7")),
992+
("mask", &ValueRef::int(128)),
993+
])),
994+
),
995+
(ValueRef::str("10.0.0/8"), ValueRef::dict(None)),
996+
(ValueRef::str("10.0.0.0/33"), ValueRef::dict(None)),
997+
(
998+
ValueRef::str("2001:db8:1:2:3:4:5:7/129"),
999+
ValueRef::dict(None),
1000+
),
1001+
(ValueRef::str("0.0.0.0/256"), ValueRef::dict(None)),
1002+
(ValueRef::str("::/256"), ValueRef::dict(None)),
1003+
(ValueRef::str("10.0.0.0/8/8"), ValueRef::dict(None)),
1004+
(ValueRef::str("2001:db8::/56/56"), ValueRef::dict(None)),
1005+
(ValueRef::str("0.0.0.0/-1"), ValueRef::dict(None)),
1006+
(ValueRef::str("::/-1"), ValueRef::dict(None)),
1007+
(ValueRef::str("10.128.0.0/8"), ValueRef::dict(None)),
1008+
(ValueRef::str("2001:db8::/16"), ValueRef::dict(None)),
1009+
(ValueRef::str("10.1.2.3/31"), ValueRef::dict(None)),
1010+
(
1011+
ValueRef::str("2001:db8:1:2:3:4:5:7/127"),
1012+
ValueRef::dict(None),
1013+
),
1014+
];
1015+
let mut ctx = Context::default();
1016+
for (cidr, expected) in cases.iter() {
1017+
unsafe {
1018+
let actual = &*kclvm_net_parse_CIDR(
1019+
&mut ctx,
1020+
&ValueRef::list(Some(&[&cidr])),
1021+
&ValueRef::dict(None),
1022+
);
1023+
assert_eq!(expected, actual, "{} positional", cidr);
1024+
}
1025+
unsafe {
1026+
let actual = &*kclvm_net_parse_CIDR(
1027+
&mut ctx,
1028+
&ValueRef::list(None),
1029+
&ValueRef::dict(Some(&[("cidr", cidr)])),
1030+
);
1031+
assert_eq!(expected, actual, "{} named", cidr);
1032+
}
1033+
}
1034+
}
1035+
1036+
#[test]
1037+
#[allow(non_snake_case)]
1038+
fn test_parse_CIDR_invalid() {
1039+
let prev_hook = std::panic::take_hook();
1040+
// Disable print panic info in stderr.
1041+
std::panic::set_hook(Box::new(|_| {}));
1042+
assert_panic(
1043+
"parse_CIDR() missing 1 required positional argument: 'cidr'",
1044+
|| {
1045+
let mut ctx = Context::new();
1046+
let args = ValueRef::list(None).into_raw(&mut ctx);
1047+
let kwargs = ValueRef::dict(None).into_raw(&mut ctx);
1048+
kclvm_net_parse_CIDR(ctx.into_raw(), args, kwargs);
1049+
},
1050+
);
1051+
std::panic::set_hook(prev_hook);
1052+
}
9371053
}

kclvm/sema/src/builtin/system_module.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ register_net_member! {
408408
range: dummy_range(),
409409
},
410410
],
411-
r#"Parse a CIDR block into network, prefix, minHost, and maxHost."#,
411+
r#"Parse a CIDR prefix into a dict containing 'ip' (the IP) and 'mask' (the prefix bit length)."#,
412412
false,
413413
None,
414414
)

0 commit comments

Comments
 (0)