I have tried to generalize the concept of turning an IP address into a boolean.The save format is an overly complicated way of storing consecutive IP addresses as strings of bitsExample usage:fetchMany (Brute 2 $Timeout second http) [read 127.0.0.0 .. read 127.0.1.0]Occy/Source.hs-- Represents a way of turning ip-- addresses into booleans---- By default, fetchMany will do-- all the requests in parallel-- use Seq to prevent thismodule Occy.Source whereimport qualified Control.Monad.Parallel as Pimport qualified Data.Bitstream         as BSimport Data.IPtype Data  = BS.Bitstream BS.Righttype Table = [(IPv4, Bool)]class Source f where  fetchOne  :: f ->  IPv4  -> IO  Bool  fetchMany :: f -> [IPv4] -> IO [Bool]  fetchOne  f = fmap head . fetchMany f . return  fetchMany f = P.mapM $ fetchOne fOccy/Source/Port.hs-- Attempts to connect to some port---- This might hang forever. Use-- Timeout to prevent thismodule Occy.Source.Port whereimport Networkimport Control.Exceptionimport System.IOimport Data.IPimport Occy.Sourcenewtype Port = Port PortNumber deriving Showinstance Source Port where  fetchOne (Port p) ip =    catch (connectTo (show ip) (PortNumber p) >>= hClose >> return True)       $ \(SomeException _) -> return Falsehttp = Port 80Occy/Source/Timeout.hs-- Evaluate a request to false-- if it doesn't return within-- some time limitmodule Occy.Source.Timeout  ( Timeout (..)  , second, ms  ) whereimport System.Timeoutimport Occy.Sourcedata Timeout f = Timeout Int finstance Source f => Source (Timeout f) where  fetchOne (Timeout n f) ip = do    x <- timeout n $ fetchOne f ip    case x of Just b -> return b              _      -> return Falsesecond = 10^6 :: Intms     = 10^3 :: IntOccy/Source/Seq.hs-- Limits a source to do N-- requests at a timemodule Occy.Source.Seq  ( Seq (..)  ) whereimport Data.List.Splitimport Control.Monadimport Occy.Sourcedata Seq f = Seq Int finstance Source f => Source (Seq f) where  fetchMany (Seq s f) = fmap join . mapM (fetchMany f) . chunksOf sOccy/Source/Static.hs-- Return values from a table,-- or get it some other waymodule Occy.Source.Static  ( Static (..)  , static  ) whereimport qualified Data.Map as Mimport Data.IPimport Occy.Sourcedata Static f = Static (M.Map IPv4 Bool) finstance Source f => Source (Static f) where  fetchOne (Static t f) ip    | Just x <- M.lookup ip t = return x    | otherwise               = fetchOne f ipstatic l = Static $ M.fromList lOccy/Source/Brute.hs-- Bruteforces request.-- It will repeatedly send requests to-- any IPs that returns false up-- to N timesmodule Occy.Source.Brute  ( Brute (..)  ) whereimport qualified Data.Map as Mimport Occy.Sourceimport Occy.Source.Staticdata Brute f = Brute Int finstance Source f => Source (Brute f) where  fetchMany (Brute n f) ips = do    xs <- fetchMany f ips    if n > 1       then fetchMany (static (filter snd $ zip ips xs) $ Brute (n-1) f) ips       else return xsOccy/Source/Cache.hs-- Caches replies so that they are-- not requested twicemodule Occy.Source.Cache  ( Cache (..)  , cache, cache'  ) whereimport qualified Data.Cache as Cimport Data.IPimport Data.Hashableimport Control.Monadimport Occy.Sourcedata Cache f = Cache f (C.Cache IPv4 Bool)instance Source f => Source (Cache f) where  fetchOne (Cache f c) ip = do    x <- C.lookup c ip    case x of Just v  -> return v              Nothing -> do                v <- fetchOne f ip                C.insert c ip v                return vinstance Hashable IPv4 where  hash = fromIntegral . toHostAddress  hashWithSalt x = (+x) . hashcache :: Source f => f -> IO (Cache f)cache = cache' []cache' :: Source f => Table -> f -> IO (Cache f)cache' t f = do  c <- C.newCache Nothing  forM_ t . uncurry $ C.insert c  return  $ Cache f cOccy/Source/Identity.hs-- The identity sourcemodule Occy.Source.Identity  ( Identity (..)  , false  ) whereimport Occy.Sourcenewtype Identity = Identity Boolinstance Source Identity where  fetchOne (Identity x) _ = return xfalse = Identity FalseOccy/Source/Disk.hs-- Read / write to diskmodule Occy.Source.Disk  ( Disk  , loadFile  , disk, save  ) whereimport qualified Data.Bitstream as BSimport Data.List hiding (groupBy)import Data.List.HTimport System.Directoryimport Data.IPimport GHC.Wordimport Occy.Sourceimport Occy.Source.Statictype Disk = Staticdisk :: Source f => FilePath -> f -> IO (Disk f)disk pth f = flip static f <$> loadFile pthsave :: FilePath -> Table -> IO ()save pth = BS.writeFile pth . BS.concat . map writeBlock . blocksloadFile :: FilePath -> IO TableloadFile pth = do  x <- doesFileExist pth  if not x    then return []    else do      src <- BS.readFile pth      return . concatMap fst             . takeWhile (not . BS.null . snd)             $ iterate (readBlock.snd) ([], src)readBlock :: Data -> (Table, Data)readBlock bs | a <- BS.toBits $ BS.take 32 bs             , b <- BS.toBits . BS.take 32 $ BS.drop 32 bs             = ( take b               . zip [fromHostAddress a ..]               . BS.unpack . BS.take b $ BS.drop 64 bs               , BS.drop (b+64) bs )blocks :: Table -> [(IPv4, [Bool])]blocks t = merge         . map (\(x:xs) -> (fst x, snd x : map snd xs))         . groupBy (\(a,_) (b,_) -> succ a == b)         $ sortOn fst tmerge :: [(IPv4, [Bool])] -> [(IPv4, [Bool])]merge = map (foldl1 $ \(a,ax) (b,bx) ->                let (ah,at) = splitAt (fromIntegral $ toHostAddress b - toHostAddress a) ax                    (bh,bt) = splitAt (length at) bx                    ct      = drop    (length bt) at                in ( a, ah                     ++ zipWith (||) at bx                     ++ if length at > length bh                        then ct                        else bt                   )            )      . groupBy (\(a,xa) (b,xb) -> toHostAddress a + fromIntegral (length xa) >= toHostAddress b)writeBlock :: (IPv4, [Bool]) -> DatawriteBlock (s, bs) =  BS.concat [ BS.fromBits $ toHostAddress s            , BS.fromBits (fromIntegral $ length bs :: Word32)            , BS.pack bs ]occy.cabalname:                occyversion:             0.1.0.0-- synopsis:-- description:homepage:            https://github.com/githubuser/occy#readmelicense:             BSD3license-file:        LICENSEauthor:              Author name heremaintainer:          example@example.comcopyright:           2017 Author name herecategory:            Webbuild-type:          Simple-- extra-source-files:  README.mdcabal-version:       >=1.10library  hs-source-dirs:      src  exposed-modules:     Occy.Source, Occy.Source.Port                     , Occy.Source.Seq, Occy.Source.Cache                     , Occy.Source.Brute, Occy.Source.Timeout                     , Occy.Source.Static, Occy.Source.Disk                     , Occy.Source.Identity  build-depends:       base >= 4.7 && < 5                     , iproute                     , bitstream                     , directory                     , bitwise                     , mtl                     , monad-loops                     , utility-ht                     , network                     , monad-parallel                     , cache                     , hashable                     , split                     , containers  default-language:    Haskell2010source-repository head  type:     git  location: https://github.com/githubuser/occystack.yamlflags: {}extra-package-dbs: []packages:- '.'extra-deps:- appar-0.1.4- base-unicode-symbols-0.2.2.4- bitstream-0.2.0.4- bitwise-0.1.1.1- byteorder-1.0.4- cache-0.1.0.0- clock-0.7.2- hashable-1.2.5.0- iproute-1.7.1- monad-loops-0.4.3- monad-parallel-0.7.2.2- mtl-2.2.1- network-2.6.3.1- parallel-3.2.1.0- primitive-0.6.1.0- split-0.2.3.1- stm-2.4.4.1- text-1.2.2.1- transformers-compat-0.5.1.4- unordered-containers-0.2.7.2- utility-ht-0.0.12- vector-0.10.12.3resolver: ghc-7.10.2
Flexible port scanner library
haskell;networking;ip address