4 import Control.Concurrent.ParallelIO.Global (
7 import Control.Monad (unless, when)
8 import qualified Data.ByteString.Char8 as BS (intercalate, pack, unpack)
9 import Data.List ((\\), intercalate)
10 import Data.Maybe (catMaybes, isNothing)
11 import Data.String.Utils (splitWs)
12 import System.Exit (ExitCode(..), exitSuccess, exitWith)
13 import System.IO (stderr, hPutStrLn)
14 import Text.Read (readMaybe)
35 import DNS (Domain, lookup_ptrs)
36 import ExitCodes ( exit_args_parse_failed, exit_invalid_cidr )
40 -- | A regular expression that matches a non-address character.
41 non_addr_char :: String
42 non_addr_char = "[^\\.0-9]"
45 -- | Add non_addr_chars on either side of the given String. This
46 -- prevents (for example) the regex '127.0.0.1' from matching
48 addr_barrier :: String -> String
49 addr_barrier x = non_addr_char ++ x ++ non_addr_char
52 -- | The magic happens here. We take a CIDR String as an argument, and
53 -- return the equivalent regular expression. We do this as follows:
55 -- 1. Compute the minimum possible value of each octet.
56 -- 2. Compute the maximum possible value of each octet.
57 -- 3. Generate a regex matching every value between those min and
59 -- 4. Join the regexes from step 3 with regexes matching periods.
60 -- 5. Stick an address boundary on either side of the result.
61 cidr_to_regex :: Cidr.Cidr -> String
63 addr_barrier (intercalate "\\." [range1, range2, range3, range4])
65 range1 = numeric_range min1 max1
66 range2 = numeric_range min2 max2
67 range3 = numeric_range min3 max3
68 range4 = numeric_range min4 max4
69 min1 = fromEnum (min_octet1 cidr)
70 min2 = fromEnum (min_octet2 cidr)
71 min3 = fromEnum (min_octet3 cidr)
72 min4 = fromEnum (min_octet4 cidr)
73 max1 = fromEnum (max_octet1 cidr)
74 max2 = fromEnum (max_octet2 cidr)
75 max3 = fromEnum (max_octet3 cidr)
76 max4 = fromEnum (max_octet4 cidr)
80 -- | Take a list of Strings, and return a regular expression matching
82 alternate :: [String] -> String
83 alternate terms = "(" ++ (intercalate "|" terms) ++ ")"
86 -- | Take two Ints as parameters, and return a regex matching any
87 -- integer between them (inclusive).
88 numeric_range :: Int -> Int -> String
90 alternate (map show [lower..upper])
98 -- First, check for any errors that occurred while parsing
99 -- the command line options.
100 errors <- CommandLine.parse_errors
101 unless (null errors) $ do
102 hPutStrLn stderr (concat errors)
103 putStrLn CommandLine.help_text
104 exitWith (ExitFailure exit_args_parse_failed)
106 -- Next, check to see if the 'help' option was passed to the
107 -- program. If it was, display the help, and exit successfully.
108 help_opt_set <- CommandLine.help_set
109 when help_opt_set $ do
110 putStrLn CommandLine.help_text
113 -- The input function we receive here should know what to read.
114 inputfunc <- (CommandLine.input_function)
117 let cidr_strings = splitWs input
118 let cidrs = map readMaybe cidr_strings
120 when (any isNothing cidrs) $ do
121 putStrLn "Error: not valid CIDR notation."
122 exitWith (ExitFailure exit_invalid_cidr)
124 -- Filter out only the valid ones.
125 let valid_cidrs = catMaybes cidrs
127 -- Get the mode of operation.
128 mode <- CommandLine.parse_mode
132 let regexes = map cidr_to_regex valid_cidrs
133 putStrLn $ alternate regexes
135 mapM_ print (combine_all valid_cidrs)
139 dupes = valid_cidrs \\ (combine_all valid_cidrs)
141 mapM_ putStrLn deletions
142 mapM_ putStrLn additions
144 dupes = valid_cidrs \\ (combine_all valid_cidrs)
145 deletions = map (\s -> '-' : (show s)) dupes
146 newcidrs = (combine_all valid_cidrs) \\ valid_cidrs
147 additions = map (\s -> '+' : (show s)) newcidrs
149 let combined_cidrs = combine_all valid_cidrs
150 let addrs = concatMap enumerate combined_cidrs
153 let combined_cidrs = combine_all valid_cidrs
154 let addrs = concatMap enumerate combined_cidrs
155 let addr_bytestrings = map (BS.pack . show) addrs
156 ptrs <- lookup_ptrs addr_bytestrings
157 let pairs = zip addr_bytestrings ptrs
158 _ <- parallel (map (putStrLn . show_pair) pairs)
164 show_pair :: (Domain, Maybe [Domain]) -> String
166 (BS.unpack s) ++ ": " ++ results
172 Just ds -> BS.unpack $ BS.intercalate space ds