From 70d32c85d421c330022bf8f59b15fd51955a53c8 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 31 Mar 2010 20:39:32 -0400 Subject: [PATCH] Commented each function. Added address barriers on the result of cidr_to_regex. --- src/hath.hs | 91 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 13 deletions(-) diff --git a/src/hath.hs b/src/hath.hs index d0c5afe..ecdb566 100644 --- a/src/hath.hs +++ b/src/hath.hs @@ -4,6 +4,9 @@ import qualified Numeric as N import System.Exit (exitFailure) import Text.Regex.Posix + +-- Stolen from ByteString. Splits a list at each element satisfying +-- the predicate p. splitWith :: (a -> Bool) -> [a] -> [[a]] splitWith p xs = ys : case zs of @@ -12,13 +15,19 @@ splitWith p xs = where (ys,zs) = break p xs +-- Takes an IP address in CIDR notation, and returns a list of its +-- octets (converted to Int). octets :: String -> [Int] octets cidr = map read (take 4 (splitWith (`elem` "./") cidr)) + +-- Returns the mask portion of a CIDR address. That is, everything +-- after the trailing slash. maskbits :: String -> Int maskbits cidr = read ((splitWith (`elem` "/") cidr) !! 1) +-- Pads a list (on the left) to length len by prepending pad_elem. pad_left_to :: Int -> a -> [a] -> [a] pad_left_to len pad_elem xs = if (length xs) >= len then @@ -28,6 +37,8 @@ pad_left_to len pad_elem xs = where padcount = len - (length xs) + +-- Pads a list (on the right) to length len by appending pad_elem. pad_right_to :: Int -> a -> [a] -> [a] pad_right_to len pad_elem xs = if (length xs) >= len then @@ -37,62 +48,86 @@ pad_right_to len pad_elem xs = where padcount = len - (length xs) - + +-- Takes an Int, and returns its base-two representation as a String. base_two :: Int -> String base_two n = N.showIntAtBase 2 DC.intToDigit n "" -ip_base_two :: [Int] -> String -ip_base_two octet_list = + +-- 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 :: [Int] -> String +octets_base_two octet_list = DL.concatMap ((pad_left_to 8 '0') .base_two) octet_list + +-- Returns the minimum address (as a base-two string) satisfying the +-- given CIDR string. min_base_two_address :: String -> String min_base_two_address cidr = pad_right_to 32 '0' netpart where - netpart = take (maskbits cidr) (ip_base_two (octets cidr)) + netpart = take (maskbits cidr) (octets_base_two (octets cidr)) + +-- Returns the maximum address (as a base-two string) satisfying the +-- given CIDR string. max_base_two_address :: String -> String max_base_two_address cidr = pad_right_to 32 '1' netpart where - netpart = take (maskbits cidr) (ip_base_two (octets cidr)) + netpart = take (maskbits cidr) (octets_base_two (octets cidr)) + +-- The octet components of min_base_two_address, as a base-two String. min_base_two_octets :: String -> [String] min_base_two_octets cidr = [octet1, octet2, octet3, octet4] where - addr = min_base_two_address cidr + addr = min_base_two_address cidr octet1 = fst (DL.splitAt 8 addr) octet2 = fst (DL.splitAt 8 (snd (DL.splitAt 8 addr))) octet3 = fst (DL.splitAt 8 (snd (DL.splitAt 16 addr))) octet4 = snd (DL.splitAt 24 addr) + +-- The octet components of max_base_two_address, as a base-two String. max_base_two_octets :: String -> [String] max_base_two_octets cidr = [octet1, octet2, octet3, octet4] where - addr = max_base_two_address cidr + addr = max_base_two_address cidr octet1 = fst (DL.splitAt 8 addr) octet2 = fst (DL.splitAt 8 (snd (DL.splitAt 8 addr))) octet3 = fst (DL.splitAt 8 (snd (DL.splitAt 16 addr))) octet4 = snd (DL.splitAt 24 addr) +-- The octet components of min_base_two_address, as Ints. min_octets :: String -> [Int] min_octets cidr = map base_two_to_base_ten (min_base_two_octets cidr) + +-- The octet components of max_base_two_address, as Ints. max_octets :: String -> [Int] max_octets cidr = map base_two_to_base_ten (max_base_two_octets cidr) + +-- 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 - + + +-- 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 @@ -103,9 +138,30 @@ base_two_to_base_ten s = parsed = N.readInt 2 is_binary_digit DC.digitToInt s +-- A regular expression that matches a non-address character. +non_addr_char :: String +non_addr_char = "[^\\.0-9]" + + +-- Add non_addr_chars on either side of the given String. This +-- prevents (for example) the regex '127.0.0.1' from matching +-- '127.0.0.100'. +addr_barrier :: String -> String +addr_barrier x = non_addr_char ++ x ++ non_addr_char + + +-- The magic happens here. We take a CIDR String as an argument, and +-- return the equivalent regular expression. We do this as follows: +-- +-- 1. Compute the minimum possible value of each octet. +-- 2. Compute the maximum possible value of each octet. +-- 3. Generate a regex matching every value between those min and +-- max values. +-- 4. Join the regexes from step 3 with regexes matching periods. +-- 5. Stick an address boundary on either side of the result. cidr_to_regex :: String -> String cidr_to_regex cidr = - range1 ++ "\\." ++ range2 ++ "\\." ++ range3 ++ "\\." ++ range4 + addr_barrier (DL.intercalate "\\." [range1, range2, range3, range4]) where range1 = numeric_range min1 max1 range2 = numeric_range min2 max2 @@ -120,24 +176,33 @@ cidr_to_regex cidr = max3 = (max_octets cidr) !! 2 max4 = (max_octets cidr) !! 3 - + +-- 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}" + +-- Take a list of Strings, and return a regular expression matching +-- any of them. alternate :: [String] -> String alternate terms = "(" ++ (concat (DL.intersperse "|" terms)) ++ ")" - + + +-- Take two Ints as parameters, and return a regex matching any +-- integer between them (inclusive). numeric_range :: Int -> Int -> String numeric_range x y = alternate (map show [lower..upper]) where lower = minimum [x,y] upper = maximum [x,y] - + + main :: IO () main = do line <- getLine - + if (is_valid_cidr line) then do putStrLn (cidr_to_regex line) -- 2.44.2