hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit ae8018ba6e6544d0b47696a00dfc594ae6c65d6f
parent 4e8aab89270a5bbfa3c3d4e300dee5c798efb2fe
Author: Miccah Castorina <contact@miccah.io>
Date:   Sun, 12 Dec 2021 21:10:37 -0600

net::ip: implement subnet_contains

Signed-off-by: Miccah Castorina <contact@miccah.io>

Diffstat:
Mnet/ip/+test.ha | 30++++++++++++++++++++++++++++++
Mnet/ip/ip.ha | 41+++++++++++++++++++++++++++++++++++++++++
2 files changed, 71 insertions(+), 0 deletions(-)

diff --git a/net/ip/+test.ha b/net/ip/+test.ha @@ -84,3 +84,33 @@ fn subnet_test_simple(s: str) void = { subnet_test_simple(subnet_tests[i]); }; }; + +@test fn test_subnet_contains() void = { + let addr_tests: [](str, str, bool) = [ + // a, b, want + ("10.10.10.0/24", "10.10.10.0", true), + ("10.10.10.0/24", "10.10.10.255", true), + ("10.10.10.0/24", "10.10.11.0", false), + ("127.0.0.1/24", "::1", false), + ("::1/8", "::1", true), + ]; + let cidr_tests: [](str, str, bool) = [ + // a, b, want + ("10.10.10.0/24", "10.10.10.0/24", true), + ("10.10.10.0/24", "10.10.10.0/25", true), + ("10.10.10.0/24", "10.10.10.0/23", false), + ("10.10.10.0/24", "10.10.11.0/24", false), + ]; + for (let i = 0z; i < len(addr_tests); i += 1) { + const input = addr_tests[i]; + let a = parsecidr(input.0)!; + let b = parse(input.1)!; + assert(subnet_contains(a, b) == input.2); + }; + for (let i = 0z; i < len(cidr_tests); i += 1) { + const input = cidr_tests[i]; + let a = parsecidr(input.0)!; + let b = parsecidr(input.1)!; + assert(subnet_contains(a, b) == input.2); + }; +}; diff --git a/net/ip/ip.ha b/net/ip/ip.ha @@ -350,3 +350,44 @@ fn wanttoken(tok: *strings::tokenizer) (str | invalid) = { return invalid; }; }; + +// Returns whether an [[addr]] (or another [[subnet]]) is contained +// within a [[subnet]]. +export fn subnet_contains(sub: subnet, item: (addr | subnet)) bool = { + let addr: subnet = match (item) { + case let addr: addr => + yield subnet{ + addr = addr, + mask = sub.mask, + }; + case let sub: subnet => + yield sub; + }; + // Get byte slices for both addresses and masks. + let ipa = match (sub.addr) { + case let v4: addr4 => yield v4[..]; + case let v6: addr6 => yield v6[..]; + }; + let maska = match (sub.mask) { + case let v4: addr4 => yield v4[..]; + case let v6: addr6 => yield v6[..]; + }; + let ipb = match (addr.addr) { + case let v4: addr4 => yield v4[..]; + case let v6: addr6 => yield v6[..]; + }; + let maskb = match (addr.mask) { + case let v4: addr4 => yield v4[..]; + case let v6: addr6 => yield v6[..]; + }; + if (len(ipa) != len(ipb) || len(maska) != len(maskb) || len(ipa) != len(maska)) { + // Mismatched addr4 and addr6 addresses / masks. + return false; + }; + for (let i = 0z; i < len(ipa); i += 1) { + if (ipa[i] & maska[i] != ipb[i] & maska[i] || maska[i] > maskb[i]) { + return false; + }; + }; + return true; +};