module Ecluse.Core.Security.IpLiteral (
IpAddr (..),
parseIpLiteral,
isDecimal,
isHex,
) where
import Data.Text qualified as T
data IpAddr
=
IpV4 Word8 Word8 Word8 Word8
|
IpV6 [Word16]
parseIpLiteral :: Text -> Maybe IpAddr
parseIpLiteral :: Text -> Maybe IpAddr
parseIpLiteral Text
host = case Text -> Maybe (Char, Text)
T.uncons Text
host of
Maybe (Char, Text)
Nothing -> Maybe IpAddr
forall a. Maybe a
Nothing
Just (Char, Text)
_ -> if (Char -> Bool) -> Text -> Bool
T.any (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
':') Text
host then Text -> Maybe IpAddr
parseIPv6 Text
host else (Text -> Maybe Word8) -> Text -> Maybe IpAddr
parseIPv4 Text -> Maybe Word8
octetInetAton Text
host
parseIPv4 :: (Text -> Maybe Word8) -> Text -> Maybe IpAddr
parseIPv4 :: (Text -> Maybe Word8) -> Text -> Maybe IpAddr
parseIPv4 Text -> Maybe Word8
octet Text
host = case HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn Text
"." Text
host of
[Text
a, Text
b, Text
c, Text
d] -> Word8 -> Word8 -> Word8 -> Word8 -> IpAddr
IpV4 (Word8 -> Word8 -> Word8 -> Word8 -> IpAddr)
-> Maybe Word8 -> Maybe (Word8 -> Word8 -> Word8 -> IpAddr)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Maybe Word8
octet Text
a Maybe (Word8 -> Word8 -> Word8 -> IpAddr)
-> Maybe Word8 -> Maybe (Word8 -> Word8 -> IpAddr)
forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Maybe Word8
octet Text
b Maybe (Word8 -> Word8 -> IpAddr)
-> Maybe Word8 -> Maybe (Word8 -> IpAddr)
forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Maybe Word8
octet Text
c Maybe (Word8 -> IpAddr) -> Maybe Word8 -> Maybe IpAddr
forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Maybe Word8
octet Text
d
[Text]
_ -> Maybe IpAddr
forall a. Maybe a
Nothing
octetInetAton :: Text -> Maybe Word8
octetInetAton :: Text -> Maybe Word8
octetInetAton Text
tok = do
n <- Maybe Integer
value
if n <= 255 then Just (fromInteger n) else Nothing
where
value :: Maybe Integer
value :: Maybe Integer
value = case Text -> Maybe (Char, Text)
T.uncons Text
tok of
Just (Char
'0', Text
rest)
| Text -> Text
T.toLower (Int -> Text -> Text
T.take Int
1 Text
rest) Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"x" ->
let hex :: Text
hex = Int -> Text -> Text
T.drop Int
1 Text
rest
in if Text -> Bool
isHex Text
hex then String -> Maybe Integer
forall a. Read a => String -> Maybe a
readMaybe (String
"0x" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
forall a. ToString a => a -> String
toString Text
hex) else Maybe Integer
forall a. Maybe a
Nothing
| Bool -> Bool
not (Text -> Bool
T.null Text
rest) ->
if Text -> Bool
isOctal Text
tok then String -> Maybe Integer
forall a. Read a => String -> Maybe a
readMaybe (String
"0o" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
forall a. ToString a => a -> String
toString Text
tok) else Maybe Integer
forall a. Maybe a
Nothing
Maybe (Char, Text)
_ -> if Text -> Bool
isDecimal Text
tok then String -> Maybe Integer
forall a. Read a => String -> Maybe a
readMaybe (Text -> String
forall a. ToString a => a -> String
toString Text
tok) else Maybe Integer
forall a. Maybe a
Nothing
octetDecimal :: Text -> Maybe Word8
octetDecimal :: Text -> Maybe Word8
octetDecimal Text
t = do
n <- if Text -> Bool
isDecimal Text
t then String -> Maybe Integer
forall a. Read a => String -> Maybe a
readMaybe (Text -> String
forall a. ToString a => a -> String
toString Text
t) else Maybe Integer
forall a. Maybe a
Nothing :: Maybe Integer
if n <= 255 then Just (fromInteger n) else Nothing
parseIPv6 :: Text -> Maybe IpAddr
parseIPv6 :: Text -> Maybe IpAddr
parseIPv6 Text
host = case HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn Text
"::" Text
host of
[Text
single] -> [Word16] -> Maybe IpAddr
exactlyEightGroups ([Word16] -> Maybe IpAddr) -> Maybe [Word16] -> Maybe IpAddr
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Text -> Maybe [Word16]
parseV6Side Text
single
[Text
before, Text
after] -> do
hd <- Text -> Maybe [Word16]
parseV6Side Text
before
tl <- parseV6Side after
expandCompressedV6 hd tl
[Text]
_ -> Maybe IpAddr
forall a. Maybe a
Nothing
parseV6Side :: Text -> Maybe [Word16]
parseV6Side :: Text -> Maybe [Word16]
parseV6Side Text
t
| Text -> Bool
T.null Text
t = [Word16] -> Maybe [Word16]
forall a. a -> Maybe a
Just []
| Bool
otherwise = [Text] -> Maybe [Word16]
parseV6Tokens (HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn Text
":" Text
t)
parseV6Tokens :: [Text] -> Maybe [Word16]
parseV6Tokens :: [Text] -> Maybe [Word16]
parseV6Tokens [] = [Word16] -> Maybe [Word16]
forall a. a -> Maybe a
Just []
parseV6Tokens [Text
tok]
| (Char -> Bool) -> Text -> Bool
T.any (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.') Text
tok = Text -> Maybe [Word16]
parseEmbeddedV4 Text
tok
| Bool
otherwise = (Word16 -> [Word16] -> [Word16]
forall a. a -> [a] -> [a]
: []) (Word16 -> [Word16]) -> Maybe Word16 -> Maybe [Word16]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Maybe Word16
parseV6Group Text
tok
parseV6Tokens (Text
tok : [Text]
rest) = (:) (Word16 -> [Word16] -> [Word16])
-> Maybe Word16 -> Maybe ([Word16] -> [Word16])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Maybe Word16
parseV6Group Text
tok Maybe ([Word16] -> [Word16]) -> Maybe [Word16] -> Maybe [Word16]
forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> [Text] -> Maybe [Word16]
parseV6Tokens [Text]
rest
parseEmbeddedV4 :: Text -> Maybe [Word16]
parseEmbeddedV4 :: Text -> Maybe [Word16]
parseEmbeddedV4 Text
t = case (Text -> Maybe Word8) -> Text -> Maybe IpAddr
parseIPv4 Text -> Maybe Word8
octetDecimal Text
t of
Just (IpV4 Word8
a Word8
b Word8
c Word8
d) -> [Word16] -> Maybe [Word16]
forall a. a -> Maybe a
Just [Word8 -> Word8 -> Word16
forall {a} {a} {a}. (Integral a, Integral a, Num a) => a -> a -> a
pair Word8
a Word8
b, Word8 -> Word8 -> Word16
forall {a} {a} {a}. (Integral a, Integral a, Num a) => a -> a -> a
pair Word8
c Word8
d]
Maybe IpAddr
_ -> Maybe [Word16]
forall a. Maybe a
Nothing
where
pair :: a -> a -> a
pair a
hi a
lo = a -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
hi a -> a -> a
forall a. Num a => a -> a -> a
* a
256 a -> a -> a
forall a. Num a => a -> a -> a
+ a -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
lo
parseV6Group :: Text -> Maybe Word16
parseV6Group :: Text -> Maybe Word16
parseV6Group Text
t = do
n <- if Text -> Bool
isHex Text
t then String -> Maybe Integer
forall a. Read a => String -> Maybe a
readMaybe (String
"0x" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
forall a. ToString a => a -> String
toString Text
t) else Maybe Integer
forall a. Maybe a
Nothing :: Maybe Integer
if n <= 0xFFFF then Just (fromInteger n) else Nothing
expandCompressedV6 :: [Word16] -> [Word16] -> Maybe IpAddr
expandCompressedV6 :: [Word16] -> [Word16] -> Maybe IpAddr
expandCompressedV6 [Word16]
hd [Word16]
tl =
let present :: Int
present = [Word16] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Word16]
hd Int -> Int -> Int
forall a. Num a => a -> a -> a
+ [Word16] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Word16]
tl
in if Int
present Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
7
then IpAddr -> Maybe IpAddr
forall a. a -> Maybe a
Just ([Word16] -> IpAddr
IpV6 ([Word16]
hd [Word16] -> [Word16] -> [Word16]
forall a. Semigroup a => a -> a -> a
<> Int -> Word16 -> [Word16]
forall a. Int -> a -> [a]
replicate (Int
8 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
present) Word16
0 [Word16] -> [Word16] -> [Word16]
forall a. Semigroup a => a -> a -> a
<> [Word16]
tl))
else Maybe IpAddr
forall a. Maybe a
Nothing
exactlyEightGroups :: [Word16] -> Maybe IpAddr
exactlyEightGroups :: [Word16] -> Maybe IpAddr
exactlyEightGroups gs :: [Word16]
gs@[Word16
_, Word16
_, Word16
_, Word16
_, Word16
_, Word16
_, Word16
_, Word16
_] = IpAddr -> Maybe IpAddr
forall a. a -> Maybe a
Just ([Word16] -> IpAddr
IpV6 [Word16]
gs)
exactlyEightGroups [Word16]
_ = Maybe IpAddr
forall a. Maybe a
Nothing
isDecimal :: Text -> Bool
isDecimal :: Text -> Bool
isDecimal Text
t = Bool -> Bool
not (Text -> Bool
T.null Text
t) Bool -> Bool -> Bool
&& (Char -> Bool) -> Text -> Bool
T.all (Char -> String -> Bool
forall (f :: * -> *) a.
(Foldable f, DisallowElem f, Eq a) =>
a -> f a -> Bool
`elem` [Char
'0' .. Char
'9']) Text
t
isOctal :: Text -> Bool
isOctal :: Text -> Bool
isOctal Text
t = Bool -> Bool
not (Text -> Bool
T.null Text
t) Bool -> Bool -> Bool
&& (Char -> Bool) -> Text -> Bool
T.all (Char -> String -> Bool
forall (f :: * -> *) a.
(Foldable f, DisallowElem f, Eq a) =>
a -> f a -> Bool
`elem` [Char
'0' .. Char
'7']) Text
t
isHex :: Text -> Bool
isHex :: Text -> Bool
isHex Text
t = Bool -> Bool
not (Text -> Bool
T.null Text
t) Bool -> Bool -> Bool
&& (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
isHexDigit Text
t
where
isHexDigit :: Char -> Bool
isHexDigit Char
c = Char
c Char -> String -> Bool
forall (f :: * -> *) a.
(Foldable f, DisallowElem f, Eq a) =>
a -> f a -> Bool
`elem` ([Char
'0' .. Char
'9'] String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [Char
'a' .. Char
'f'] String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [Char
'A' .. Char
'F'])