{-# LANGUAGE OverloadedLists , OverloadedStrings , RecordWildCards #-} module Data.Attoparsec.IP (ipv4, ipv6) where import Data.Attoparsec.Text (Parser, char, string, digit, hexadecimal, many1) import Data.Word (Word8, Word16) import Data.Vector (Vector) import qualified Data.Vector as V import Data.Monoid ((<>)) import Control.Applicative ((<|>)) import Control.Monad (void) import Net.Types (IPv4, IPv6) import qualified Net.IPv4 as IPv4 import qualified Net.IPv6 as IPv6 ipv4 :: Parser IPv4 ipv4 = do a <- octet void (char '.') b <- octet void (char '.') c <- octet void (char '.') d <- octet pure (IPv4.fromOctets a b c d) where octet :: Parser Word8 octet = do (a,b,c) <- let oneDigit = do n <- digit pure ('0','0',n) twoDigit = do n <- digit m <- digit pure ('0',n,m) threeDigit = do n <- digit m <- digit o <- digit pure (n,m,o) in threeDigit <|> twoDigit <|> oneDigit let n :: Word8 n = read (a:b:c:[]) pure n data IPv6Divider = DividerColon | DoubleColon deriving (Show) parseDividerOrDoubleColon :: Parser IPv6Divider parseDividerOrDoubleColon = let divider = DividerColon <$ char ':' doubleColon = DoubleColon <$ string "::" in doubleColon <|> divider parseHextet :: Parser IPv6Chunk parseHextet = Hextet <$> hexadecimal parseHextets :: Parser [IPv6Chunk] parseHextets = many1 (parseHextet <|> (Divider <$> parseDividerOrDoubleColon)) data IPv6Chunk = Hextet Word16 | Divider IPv6Divider deriving (Show) data IPv6TokenPos = Init | A | B | C | D | E | F | G | Finished deriving (Show, Eq, Ord, Enum, Bounded) data IPv6State = IPv6State { hextets :: Vector Word16 , current :: IPv6TokenPos , doublePos :: Maybe IPv6TokenPos } deriving (Show) initIPv6State :: IPv6State initIPv6State = IPv6State [] Init Nothing accumIPv6State :: [IPv6Chunk] -> IPv6State accumIPv6State xs = let go :: IPv6State -> IPv6Chunk -> IPv6State go xss@IPv6State{..} x = case x of Divider d -> case d of DoubleColon -> xss { doublePos = Just current } DividerColon -> xss Hextet n -> xss { hextets = V.snoc hextets n, current = succ current } in foldl go initIPv6State xs ipv6StateToIPv6 :: IPv6State -> Maybe IPv6 ipv6StateToIPv6 IPv6State{..} = case doublePos of Nothing -> case V.toList hextets of (a:b:c:d:e:f:g:h:_) -> Just (IPv6.fromWord16s a b c d e f g h) _ -> Nothing Just p -> let zeros = V.replicate (8 - V.length hextets) 0 composite = case p of Init -> Just (zeros <> hextets) A -> case V.toList hextets of (a:hs) -> Just ([a] <> zeros <> V.fromList hs) _ -> Nothing B -> case V.toList hextets of (a:b:hs) -> Just ([a,b] <> zeros <> V.fromList hs) _ -> Nothing C -> case V.toList hextets of (a:b:c:hs) -> Just ([a,b,c] <> zeros <> V.fromList hs) _ -> Nothing D -> case V.toList hextets of (a:b:c:d:hs) -> Just ([a,b,c,d] <> zeros <> V.fromList hs) _ -> Nothing E -> case V.toList hextets of (a:b:c:d:e:hs) -> Just ([a,b,c,d,e] <> zeros <> V.fromList hs) _ -> Nothing F -> case V.toList hextets of (a:b:c:d:e:f:hs) -> Just ([a,b,c,d,e,f] <> zeros <> V.fromList hs) _ -> Nothing G -> case V.toList hextets of (a:b:c:d:e:f:g:hs) -> Just ([a,b,c,d,e,f,g] <> zeros <> V.fromList hs) _ -> Nothing Finished -> case V.toList hextets of (a:b:c:d:e:f:g:h:_) -> Just [a,b,c,d,e,f,g,h] _ -> Nothing in case V.toList <$> composite of Just (a:b:c:d:e:f:g:h:_) -> Just (IPv6.fromWord16s a b c d e f g h) _ -> Nothing ipv6 :: Parser IPv6 ipv6 = do s <- parseHextets case toIPv6 s of Nothing -> fail "Not an IPv6" Just x -> pure x where toIPv6 :: [IPv6Chunk] -> Maybe IPv6 toIPv6 = ipv6StateToIPv6 . accumIPv6State