{ lib, internal, ip, cidr, }: let inherit (builtins) elemAt filter length match split tryEval ; inherit (lib) all toInt; in rec { /** Check if a string is a valid IPv4 address. # Type ``` isValidIp :: Any -> Bool ``` # Example ```nix isValidIp "192.168.1.1" => true ``` */ isValidIp = str: if !builtins.isString str then false else let # Match basic format: digits.digits.digits.digits m = match "([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)" str; in if m == null then false else let octets = map toInt m; validOctet = o: o >= 0 && o <= 255; in all validOctet octets; /** Check if a string is valid CIDR notation. # Type ``` isValidCidr :: Any -> Bool ``` # Example ```nix isValidCidr "192.168.1.0/24" => true ``` */ isValidCidr = str: if !builtins.isString str then false else let parts = filter builtins.isString (split "/" str); in if length parts != 2 then false else let ipPart = elemAt parts 0; prefixPart = elemAt parts 1; prefixMatch = match "[0-9]+" prefixPart; in if !isValidIp ipPart then false else if prefixMatch == null then false else let prefix = toInt prefixPart; in prefix >= 0 && prefix <= 32; /** Try to parse an IP, return null on failure. # Type ``` tryParseIp :: String -> (Int | Null) ``` # Example ```nix tryParseIp "192.168.1.1" => 3232235777 ``` */ tryParseIp = str: let result = tryEval (if isValidIp str then ip.parse str else throw "invalid"); in if result.success then result.value else null; /** Try to parse a CIDR, return null on failure. # Type ``` tryParseCidr :: String -> ({ address :: Int; prefixLen :: Int; } | Null) ``` # Example ```nix tryParseCidr "192.168.1.0/24" => { address = 3232235776; prefixLen = 24; } ``` */ tryParseCidr = str: let result = tryEval (if isValidCidr str then cidr.parse str else throw "invalid"); in if result.success then result.value else null; /** Check if IP is in RFC 1918 private address space. Covers 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16. # Type ``` isPrivate :: (String | Int) -> Bool ``` # Example ```nix isPrivate "192.168.1.1" => true ``` */ isPrivate = addr: let ipInt = ip.parse addr; in cidr.contains "10.0.0.0/8" ipInt || cidr.contains "172.16.0.0/12" ipInt || cidr.contains "192.168.0.0/16" ipInt; /** Check if IP is loopback (127.0.0.0/8). # Type ``` isLoopback :: (String | Int) -> Bool ``` # Example ```nix isLoopback "127.0.0.1" => true ``` */ isLoopback = addr: cidr.contains "127.0.0.0/8" (ip.parse addr); /** Check if IP is link-local (169.254.0.0/16). # Type ``` isLinkLocal :: (String | Int) -> Bool ``` # Example ```nix isLinkLocal "169.254.1.1" => true ``` */ isLinkLocal = addr: cidr.contains "169.254.0.0/16" (ip.parse addr); /** Check if IP is multicast (224.0.0.0/4). # Type ``` isMulticast :: (String | Int) -> Bool ``` # Example ```nix isMulticast "224.0.0.1" => true ``` */ isMulticast = addr: cidr.contains "224.0.0.0/4" (ip.parse addr); /** Check if IP is broadcast (255.255.255.255). # Type ``` isBroadcast :: (String | Int) -> Bool ``` # Example ```nix isBroadcast "255.255.255.255" => true ``` */ isBroadcast = addr: (ip.parse addr) == 4294967295; /** Check if IP is unspecified (0.0.0.0). # Type ``` isUnspecified :: (String | Int) -> Bool ``` # Example ```nix isUnspecified "0.0.0.0" => true ``` */ isUnspecified = addr: (ip.parse addr) == 0; /** Check if IP is documentation range. Covers 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24. # Type ``` isDocumentation :: (String | Int) -> Bool ``` # Example ```nix isDocumentation "192.0.2.1" => true ``` */ isDocumentation = addr: let ipInt = ip.parse addr; in cidr.contains "192.0.2.0/24" ipInt || cidr.contains "198.51.100.0/24" ipInt || cidr.contains "203.0.113.0/24" ipInt; /** Check if IP is reserved for future use (240.0.0.0/4, except broadcast). # Type ``` isReserved :: (String | Int) -> Bool ``` # Example ```nix isReserved "240.0.0.1" => true ``` */ isReserved = addr: let ipInt = ip.parse addr; in cidr.contains "240.0.0.0/4" ipInt && ipInt != 4294967295; /** Check if IP is globally routable (not private, loopback, link-local, etc.). # Type ``` isGlobalUnicast :: (String | Int) -> Bool ``` # Example ```nix isGlobalUnicast "8.8.8.8" => true ``` */ isGlobalUnicast = addr: !isPrivate addr && !isLoopback addr && !isLinkLocal addr && !isMulticast addr && !isBroadcast addr && !isUnspecified addr && !isDocumentation addr && !isReserved addr; /** Classify an IP address into its type. Returns one of: "loopback", "private", "link-local", "multicast", "broadcast", "unspecified", "documentation", "reserved", "global-unicast". # Type ``` classify :: (String | Int) -> String ``` # Example ```nix classify "192.168.1.1" => "private" ``` */ classify = addr: if isLoopback addr then "loopback" else if isPrivate addr then "private" else if isLinkLocal addr then "link-local" else if isMulticast addr then "multicast" else if isBroadcast addr then "broadcast" else if isUnspecified addr then "unspecified" else if isDocumentation addr then "documentation" else if isReserved addr then "reserved" else "global-unicast"; }