attoparsec-ip-0.0.2: src/Data/Attoparsec/IP.hs
{-# 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