{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module HaskellWorks.Data.Network.Ip.Ipv6
( Ipv6Address(..)
, Ipv6NetMask(..)
, Ipv6Block(..)
, ipv4BlockToMappedIpv6Block
, parseIpv6Block
, masksIpv6
, isValidIpv6Block
) where
import Control.Applicative
import Control.Monad
import Data.Bits
import Data.Char
import Data.Generics.Product.Any
import Data.Maybe
import Data.Word
import GHC.Generics
import Prelude hiding (words)
import Text.Read
import qualified Data.Attoparsec.Text as AP
import qualified Data.Bits as B
import qualified Data.IP as D
import qualified Data.String as S
import qualified Data.Text as T
import qualified HaskellWorks.Data.Network.Ip.Internal as I
import qualified HaskellWorks.Data.Network.Ip.Ipv4 as I4
import qualified Text.ParserCombinators.ReadPrec as RP
newtype Ipv6Address = Ipv6Address
{ words :: (Word32, Word32, Word32, Word32)
} deriving (Eq, Ord, Generic)
instance Show Ipv6Address where
showsPrec _ (Ipv6Address w) = shows (D.fromHostAddress6 w)
instance Read Ipv6Address where
readsPrec :: Int -> String -> [(Ipv6Address, String)]
readsPrec _ s =
case readMaybe s :: Maybe D.IPv6 of
Just ip -> [(Ipv6Address (D.toHostAddress6 ip), "")]
Nothing -> []
newtype Ipv6NetMask = Ipv6NetMask
{ word :: Word8
} deriving (Enum, Eq, Ord, Show, Generic)
instance Read Ipv6NetMask where
readsPrec _ s =
case Ipv6NetMask <$> m of
Just maskv6 -> [(maskv6, "")]
Nothing -> []
where
m = mfilter (\a -> a >= 0 && a <= 128) (readMaybe s)
data Ipv6Block = Ipv6Block
{ base :: !Ipv6Address
, mask :: !Ipv6NetMask
} deriving (Eq, Ord, Generic)
instance Read Ipv6Block where
readsPrec _ s =
case T.unpack <$> T.split (== '/') (T.pack s) of
[addr, mask] ->
case readMaybe addr :: Maybe Ipv6Address of
Just ipv6 ->
case readMaybe mask of
Just maskv6 ->
let i6b = Ipv6Block ipv6 maskv6 in
[(i6b, "") | isValidIpv6Block i6b]
Nothing -> []
Nothing -> []
_ -> []
instance Show Ipv6Block where
showsPrec _ (Ipv6Block b (Ipv6NetMask m)) = shows b . ('/':) . shows m
parseIpv6Block :: T.Text -> Either T.Text Ipv6Block
parseIpv6Block t =
case T.unpack <$> T.split (== '/') t of
[addr, mask] ->
case readMaybe addr :: Maybe Ipv6Address of
Just ipv6 ->
case readMaybe mask of
Just maskv6 -> Right $ Ipv6Block ipv6 maskv6
Nothing -> Left "cannot read mask"
Nothing -> Left "cannot read addr"
_ -> Left "invalid input string"
masksIpv6 :: Word8 -> [Word32]
masksIpv6 m =
let e = 0xFFFFFFFF :: Word32
maskValue bits = e `shiftR` (32 - bits) in
if m < 32 then
[maskValue (32 - fromIntegral m), e, e, e]
else if m < 64 then
[0, maskValue (64 - fromIntegral m), e, e]
else if m < 96 then
[0, 0, maskValue (96 - fromIntegral m), e]
else if m < 128 then
[0, 0, 0, maskValue (128 - fromIntegral m)]
else
[0, 0, 0, 0]
isValidIpv6Block :: Ipv6Block -> Bool
isValidIpv6Block (Ipv6Block b (Ipv6NetMask m)) =
let lt = masksIpv6 m
ipv6 = I.word32x4ToWords (words b) in
ipv6 == zipWith (.&.) ipv6 (zipWith xor ipv6 lt)
ipv4BlockToMappedIpv6Block :: I4.Ipv4Block -> Ipv6Block
ipv4BlockToMappedIpv6Block (I4.Ipv4Block b m) =
Ipv6Block (Ipv6Address (0, 0, 0xFFFF, I4.word b)) (Ipv6NetMask (96 + I4.word8 m))