module Cidr ( Cidr, from_string, is_valid_cidr, min_first_octet, max_first_octet, min_second_octet, max_second_octet, min_third_octet, max_third_octet, min_fourth_octet, max_fourth_octet ) where import Data.Char (digitToInt, intToDigit) import Numeric (readInt, showIntAtBase) import Text.Regex.Posix import ListUtils type Maskbits = Int type Octet = Int type OctetList = (Octet, Octet, Octet, Octet) type BaseTwoOctetList = (String, String, String, String) data Cidr = Cidr { octet1 :: Octet, octet2 :: Octet, octet3 :: Octet, octet4 :: Octet, maskbits :: Maskbits } deriving (Show) -- Will return True if the passed String is in CIDR notation, False -- otherwise. is_valid_cidr :: String -> Bool is_valid_cidr cidr = cidr =~ "([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}" first :: (a,b,c,d) -> a first (w,_,_,_) = w second :: (a,b,c,d) -> b second (_,x,_,_) = x third :: (a,b,c,d) -> c third (_,_,y,_) = y fourth :: (a,b,c,d) -> d fourth (_,_,_,z) = z min_first_octet :: Cidr -> Octet min_first_octet cidr = first (min_octets cidr) min_second_octet :: Cidr -> Octet min_second_octet cidr = second (min_octets cidr) min_third_octet :: Cidr -> Octet min_third_octet cidr = third (min_octets cidr) min_fourth_octet :: Cidr -> Octet min_fourth_octet cidr = fourth (min_octets cidr) max_first_octet :: Cidr -> Octet max_first_octet cidr = first (max_octets cidr) max_second_octet :: Cidr -> Octet max_second_octet cidr = second (max_octets cidr) max_third_octet :: Cidr -> Octet max_third_octet cidr = third (max_octets cidr) max_fourth_octet :: Cidr -> Octet max_fourth_octet cidr = fourth (max_octets cidr) -- Returns the mask portion of a CIDR address. That is, everything -- after the trailing slash. maskbits_from_string :: String -> Maskbits maskbits_from_string s = read ((splitWith (`elem` "/") s) !! 1) -- Takes an IP address String in CIDR notation, and returns a list of -- its octets (converted to Int). octets_from_string :: String -> [Octet] octets_from_string s = map read (take 4 (splitWith (`elem` "./") s)) from_string :: String -> Cidr from_string s = Cidr (octs !! 0) (octs !! 1) (octs !! 2) (octs !! 3) mbits where octs = octets_from_string s mbits = maskbits_from_string s -- The base_two_to_base_ten function requires a way to determine -- whether or not the character it's currently parsing is valid. This -- should do it. is_binary_digit :: Char -> Bool is_binary_digit c = if c `elem` ['0','1'] then True else False -- Takes an Int, and returns its base-two representation as a String. base_two :: Int -> String base_two n = showIntAtBase 2 intToDigit n "" -- Takes a set of octets, and converts them to base-two -- individually. The results are then zero-padded on the left to 8 -- characters, and concatenated together. octets_base_two :: Cidr -> String octets_base_two cidr = s1 ++ s2 ++ s3 ++ s4 where s1 = ((pad_left_to 8 '0') . base_two) (octet1 cidr) s2 = ((pad_left_to 8 '0') . base_two) (octet2 cidr) s3 = ((pad_left_to 8 '0') . base_two) (octet3 cidr) s4 = ((pad_left_to 8 '0') . base_two) (octet4 cidr) base_two_octetlist_to_octetlist :: BaseTwoOctetList -> OctetList base_two_octetlist_to_octetlist b2ol = (oct1, oct2, oct3, oct4) where oct1 = base_two_to_base_ten (first b2ol) oct2 = base_two_to_base_ten (second b2ol) oct3 = base_two_to_base_ten (third b2ol) oct4 = base_two_to_base_ten (fourth b2ol) -- Convert a base-two String to an Int. base_two_to_base_ten :: String -> Int base_two_to_base_ten s = if (length parsed) == 0 then 0 else fst (parsed !! 0) where parsed = readInt 2 is_binary_digit digitToInt s -- Returns the minimum address (as a base-two string) satisfying the -- given CIDR string. min_base_two_address :: Cidr -> String min_base_two_address cidr = pad_right_to 32 '0' netpart where netpart = take (maskbits cidr) (octets_base_two cidr) -- Returns the maximum address (as a base-two string) satisfying the -- given CIDR string. max_base_two_address :: Cidr -> String max_base_two_address cidr = pad_right_to 32 '1' netpart where netpart = take (maskbits cidr) (octets_base_two cidr) -- The octet components of min_base_two_address, as a base-two String. min_base_two_octets :: Cidr -> BaseTwoOctetList min_base_two_octets cidr = (oct1, oct2, oct3, oct4) where addr = min_base_two_address cidr oct1 = fst (splitAt 8 addr) oct2 = fst (splitAt 8 (snd (splitAt 8 addr))) oct3 = fst (splitAt 8 (snd (splitAt 16 addr))) oct4 = snd (splitAt 24 addr) -- The octet components of max_base_two_address, as a base-two String. max_base_two_octets :: Cidr -> BaseTwoOctetList max_base_two_octets cidr = (oct1, oct2, oct3, oct4) where addr = max_base_two_address cidr oct1 = fst (splitAt 8 addr) oct2 = fst (splitAt 8 (snd (splitAt 8 addr))) oct3 = fst (splitAt 8 (snd (splitAt 16 addr))) oct4 = snd (splitAt 24 addr) -- The octet components of min_base_two_address, as Ints. min_octets :: Cidr -> OctetList min_octets cidr = base_two_octetlist_to_octetlist (min_base_two_octets cidr) -- The octet components of max_base_two_address, as Ints. max_octets :: Cidr -> OctetList max_octets cidr = base_two_octetlist_to_octetlist (max_base_two_octets cidr)