101 lines
No EOL
2.6 KiB
Haskell
101 lines
No EOL
2.6 KiB
Haskell
module Main where
|
|
|
|
import Net.IP ( IP )
|
|
import qualified Net.IP as IP
|
|
import qualified Net.IPv4 as NIPv4
|
|
import qualified Net.IPv6 as NIPv6
|
|
import qualified Data.Text as T
|
|
|
|
import System.Directory
|
|
( doesFileExist, getXdgDirectory, XdgDirectory(XdgConfig) )
|
|
import System.FilePath ((</>))
|
|
|
|
import Data.Set (empty, Set, insert, delete, toList)
|
|
import Data.String ( IsString(fromString) )
|
|
import Data.Maybe (catMaybes)
|
|
|
|
data Bound
|
|
= Inbound
|
|
| Outbound
|
|
deriving (Show, Eq, Ord)
|
|
|
|
data LogRecord
|
|
= Connected Bound IP
|
|
| Disconnected Bound IP
|
|
deriving (Show, Eq)
|
|
|
|
parseBound :: String -> Maybe Bound
|
|
parseBound "Inbound" = Just Inbound
|
|
parseBound "Outbound" = Just Outbound
|
|
parseBound _ = Nothing
|
|
|
|
parseLogRecord :: String -> Maybe LogRecord
|
|
parseLogRecord s =
|
|
case words s of
|
|
["Connected", b, ipTxt] -> do
|
|
bound <- parseBound b
|
|
ip <- IP.decode . fromString $ ipTxt
|
|
pure (Connected bound ip)
|
|
["Disconnected", b, ipTxt] -> do
|
|
bound <- parseBound b
|
|
ip <- IP.decode . fromString $ ipTxt
|
|
pure (Disconnected bound ip)
|
|
_ -> Nothing
|
|
|
|
type Connection = (Bound, IP)
|
|
|
|
data SuperIP
|
|
= Address IP
|
|
| Range4 NIPv4.IPv4Range
|
|
| Range6 NIPv6.IPv6Range
|
|
|
|
inSuper :: SuperIP -> IP -> Bool
|
|
inSuper (Address i0) i1 = i0 == i1
|
|
inSuper (Range4 r4) ip =
|
|
IP.case_
|
|
(\ip4 -> NIPv4.member ip4 r4)
|
|
(\_ -> False)
|
|
ip
|
|
inSuper (Range6 r6) ip =
|
|
IP.case_
|
|
(\_ -> False)
|
|
(\ip6 -> NIPv6.member ip6 r6)
|
|
ip
|
|
|
|
instance Read SuperIP where
|
|
readsPrec :: Int -> ReadS SuperIP
|
|
readsPrec _ c
|
|
| Just i <- IP.decode s = [(Address i, "")]
|
|
| Just r4 <- NIPv4.decodeRange s = [(Range4 r4, "")]
|
|
| Just r6 <- NIPv6.decodeRange s = [(Range6 r6, "")]
|
|
| otherwise = []
|
|
where
|
|
s = fromString c
|
|
|
|
instance Show SuperIP where
|
|
show (Address ip) = show ip
|
|
show (Range4 r) = show r
|
|
show (Range6 r) = show r
|
|
|
|
f :: Set Connection -> LogRecord -> Set Connection
|
|
f s (Connected b ip) = insert (b, ip) s
|
|
f s (Disconnected b ip) = delete (b, ip) s
|
|
|
|
main :: IO ()
|
|
main = do
|
|
dir <- getXdgDirectory XdgConfig "yggscraper"
|
|
let path = dir </> "blacklist"
|
|
exists <- doesFileExist path
|
|
blacklist <- if exists
|
|
then do
|
|
blacklistFile <- readFile (dir </> "blacklist")
|
|
return . map (\x -> read x :: SuperIP) . lines $ blacklistFile
|
|
else
|
|
return []
|
|
|
|
strs <- getContents
|
|
let xs = catMaybes . map parseLogRecord . lines $ strs
|
|
online = toList $ foldl' f empty xs
|
|
finder (_, ip) = any (\x -> inSuper x ip)
|
|
notBlacklisted = [x | x <- online, not $ finder x blacklist]
|
|
putStrLn . T.unpack . T.unlines . map (IP.encode . snd) $ notBlacklisted |