module Data.IP.Addr where
import Control.Monad
import Data.Bits
import Data.Char
import Data.List (foldl')
import Data.String
import Data.Word
import Text.Appar.String
import Text.Printf
data IP = IPv4 { ipv4 :: IPv4 }
| IPv6 { ipv6 :: IPv6 }
deriving (Eq)
instance Show IP where
show (IPv4 ip) = show ip
show (IPv6 ip) = show ip
type IPv4Addr = Word32
type IPv6Addr = (Word32,Word32,Word32,Word32)
newtype IPv4 = IP4 IPv4Addr deriving (Eq, Ord)
newtype IPv6 = IP6 IPv6Addr deriving (Eq, Ord)
instance Show IPv4 where
show = showIPv4
instance Show IPv6 where
show = showIPv6
showIPv4 :: IPv4 -> String
showIPv4 (IP4 a) = show4 a
where
remQuo x = (x `mod` 256, x `div` 256)
show4 q = let (a4,q4) = remQuo q
(a3,q3) = remQuo q4
(a2,q2) = remQuo q3
(a1, _) = remQuo q2
in printf "%d.%d.%d.%d" a1 a2 a3 a4
showIPv6 :: IPv6 -> String
showIPv6 (IP6 (a1,a2,a3,a4)) = show6 a1 ++ ":" ++ show6 a2 ++ ":" ++ show6 a3 ++ ":" ++ show6 a4
where
remQuo x = (x `mod` 65536, x `div` 65536)
show6 q = let (r2,q2) = remQuo q
(r1, _) = remQuo q2
in printf "%02x:%02x" r1 r2
toIPv4 :: [Int] -> IPv4
toIPv4 = IP4 . toWord32
where
toWord32 [a1,a2,a3,a4] = fromIntegral $ shift a1 24 + shift a2 16 + shift a3 8 + a4
toWord32 _ = error "toWord32"
toIPv6 :: [Int] -> IPv6
toIPv6 ad = let [x1,x2,x3,x4] = map toWord32 $ split2 ad
in IP6 (x1,x2,x3,x4)
where
split2 [] = []
split2 x = take 2 x : split2 (drop 2 x)
toWord32 [a1,a2] = fromIntegral $ shift a1 16 + a2
toWord32 _ = error "toWord32"
instance Read IP where
readsPrec _ = parseIP
instance Read IPv4 where
readsPrec _ = parseIPv4
instance Read IPv6 where
readsPrec _ = parseIPv6
parseIP :: String -> [(IP,String)]
parseIP cs =
case runParser ip4 cs of
(Just ip,rest) -> [(IPv4 ip,rest)]
(Nothing,_) -> case runParser ip6 cs of
(Just ip,rest) -> [(IPv6 ip,rest)]
(Nothing,_) -> error $ "parseIP" ++ cs
parseIPv4 :: String -> [(IPv4,String)]
parseIPv4 cs = case runParser ip4 cs of
(Nothing,_) -> error $ "parseIPv4 " ++ cs
(Just a4,rest) -> [(a4,rest)]
parseIPv6 :: String -> [(IPv6,String)]
parseIPv6 cs = case runParser ip6 cs of
(Nothing,_) -> error $ "parseIPv6 " ++ cs
(Just a6,rest) -> [(a6,rest)]
instance IsString IP where
fromString = read
instance IsString IPv4 where
fromString = read
instance IsString IPv6 where
fromString = read
dig :: Parser Int
dig = 0 <$ char '0'
<|> toInt <$> oneOf ['1'..'9'] <*> many digit
where
toInt n ns = foldl' (\x y -> x * 10 + y) 0 . map digitToInt $ n : ns
ip4 :: Parser IPv4
ip4 = do
as <- dig `sepBy1` char '.'
check as
return $ toIPv4 as
where
test errmsg adr = when (adr < 0 || 255 < adr) (fail errmsg)
check as = do let errmsg = "IPv4 adddress"
when (length as /= 4) (fail errmsg)
mapM_ (test errmsg) as
hex :: Parser Int
hex = do ns <- some hexDigit
check ns
let ms = map digitToInt ns
val = foldl' (\x y -> x * 16 + y) 0 ms
return val
where
check ns = when (length ns > 4) (fail "IPv6 address -- more than 4 hex")
ip6 :: Parser IPv6
ip6 = toIPv6 <$> ip6'
ip6' :: Parser [Int]
ip6' = do colon2
bs <- option [] hexcolon
format [] bs
<|> try (do rs <- hexcolon
check rs
return rs)
<|> do bs1 <- hexcolon2
bs2 <- option [] hexcolon
format bs1 bs2
where
colon2 = string "::"
hexcolon = hex `sepBy1` char ':'
hexcolon2 = manyTill (hex <* char ':') (char ':')
format bs1 bs2 = do let len1 = length bs1
len2 = length bs2
when (len1 > 7) (fail "IPv6 address1")
when (len2 > 7) (fail "IPv6 address2")
let len = 8 len1 len2
when (len <= 0) (fail "IPv6 address3")
let spring = replicate len 0
return $ bs1 ++ spring ++ bs2
check bs = when (length bs /= 8) (fail "IPv6 address4")