IPv6 addresses: verify the number of chunks + shortened representation.

master
Philippe Pittoli 2024-01-23 19:25:23 +01:00
parent ef1a0e40a3
commit c060ffb3cc
3 changed files with 41 additions and 14 deletions

View File

@ -1,6 +1,6 @@
module GenericParser.BaseFunctions where module GenericParser.BaseFunctions where
import Prelude (between, (<>), (==)) import Prelude (between, (-), (<), (<>), (==), (||), ($))
import Data.Array as A import Data.Array as A
import Data.String.CodeUnits (singleton) import Data.String.CodeUnits (singleton)
@ -17,6 +17,9 @@ isLower = between 'a' 'z'
isUpper :: Char -> Boolean isUpper :: Char -> Boolean
isUpper = between 'A' 'Z' isUpper = between 'A' 'Z'
isHexaDecimal :: Char -> Boolean
isHexaDecimal c = between '0' '9' c || between 'a' 'f' c || between 'A' 'F' c
isAlpha :: Char -> Boolean isAlpha :: Char -> Boolean
isAlpha c = A.any (\f -> f c) [isLower, isUpper] isAlpha c = A.any (\f -> f c) [isLower, isUpper]
@ -26,3 +29,6 @@ isAlphaNum c = A.any (\f -> f c) [isAlpha, isDigit]
isSpace :: Char -> Boolean isSpace :: Char -> Boolean
isSpace c = A.any (\v -> v == c) [' ', '\t', ' ', '\r', '\n'] isSpace c = A.any (\v -> v == c) [' ', '\t', ' ', '\r', '\n']
repeat :: forall a. Int -> a -> Array a
repeat 0 _ = []
repeat n v = if n < 0 then [] else A.cons v $ repeat (n - 1) v

View File

@ -1,9 +1,9 @@
-- | `IPAddress` is a parser for internet protocol addresses (both IPv4 and IPv6). -- | `IPAddress` is a parser for internet protocol addresses (both IPv4 and IPv6).
module GenericParser.IPAddress where module GenericParser.IPAddress where
import Prelude (between, (||), bind, pure, ($), (<<<), (>), show, map) import Prelude (Ordering(..), compare, (+), (-), bind, pure, ($), (<<<), (>), show, map)
-- import Control.Alt ((<|>)) import Control.Alt ((<|>))
-- import Control.Lazy (defer) -- import Control.Lazy (defer)
import Data.Array as A import Data.Array as A
-- import Data.Either (Either(..)) -- import Data.Either (Either(..))
@ -12,23 +12,23 @@ import Data.Maybe (Maybe(..))
import Data.String.CodeUnits as CU import Data.String.CodeUnits as CU
import GenericParser.Parser (Parser(..) import GenericParser.Parser (Parser(..)
, failureError , failureError, failure
, current_position , current_position
, tryMaybe , tryMaybe
, many1 , many1
, sat, char, nat) , sat, char, nat)
import GenericParser.BaseFunctions (repeat, isHexaDecimal)
data IPv6Error data IPv6Error
= InvalidCharacter = InvalidCharacter
| TooManyHexaDecimalCharacters | TooManyHexaDecimalCharacters
| NotEnoughChunks
isHexaDecimal :: Char -> Boolean | TooManyChunks
isHexaDecimal c = between '0' '9' c || between 'a' 'f' c || between 'A' 'F' c
hex :: forall e. Parser e Char hex :: forall e. Parser e Char
hex = sat isHexaDecimal hex = sat isHexaDecimal
-- | `ipv6_chunk` analyses just a nibble -- | `ipv6_chunk` analyses just a nibble
ipv6_chunk :: Parser IPv6Error String ipv6_chunk :: Parser IPv6Error String
ipv6_chunk = do pos <- current_position ipv6_chunk = do pos <- current_position
hexachars <- many1 hex hexachars <- many1 hex
@ -37,16 +37,34 @@ ipv6_chunk = do pos <- current_position
then Parser \_ -> failureError pos (Just TooManyHexaDecimalCharacters) then Parser \_ -> failureError pos (Just TooManyHexaDecimalCharacters)
else pure $ CU.fromCharArray hexachars else pure $ CU.fromCharArray hexachars
-- | TODO: `ipv6` currently is oversimplistic and lacks shortened representation (with doubled ':' character). ipv6_full :: Parser IPv6Error String
-- | ipv6_full = do chunks <- many1 ipv6_chunk
-- | TODO: check for double ':' pos <- current_position
-- | followup <- tryMaybe $ char ':'
case followup of
Just _ -> Parser \_ -> failure pos
Nothing -> do
case compare (A.length chunks) 8 of
LT -> Parser \_ -> failureError pos (Just NotEnoughChunks)
EQ -> pure $ A.fold (A.intersperse ":" chunks)
GT -> Parser \_ -> failureError pos (Just TooManyChunks)
ipv6_shortened :: Parser IPv6Error String
ipv6_shortened =
do chunks_part1 <- many1 ipv6_chunk
_ <- char ':'
chunks_part2 <- many1 ipv6_chunk
let part1 = A.fold (A.intersperse ":" (chunks_part1))
part2 = A.fold (A.intersperse ":" (chunks_part2))
nb_zero_filling = 8 - (A.length chunks_part1 + A.length chunks_part2)
filling = A.fold (A.intersperse ":" $ repeat nb_zero_filling "0000")
pure $ A.fold (A.intersperse ":" [part1, filling, part2])
-- | TODO: verify the number of chunks (groups of hexadecimal characters). -- | TODO: verify the number of chunks (groups of hexadecimal characters).
-- | -- |
-- | TODO: accept IPv6 addresses between brackets ([ipv6]). -- | TODO: accept IPv6 addresses between brackets ([ipv6]).
ipv6 :: Parser IPv6Error String ipv6 :: Parser IPv6Error String
ipv6 = do chunks <- many1 ipv6_chunk ipv6 = ipv6_shortened <|> ipv6_full
pure $ A.fold (A.intersperse ":" chunks)
data IPv4Error data IPv4Error

View File

@ -44,6 +44,8 @@ test_series l p v e a = foreachE a (\s -> logtest l p s v e)
showerror_ipv6 :: IP.IPv6Error -> String showerror_ipv6 :: IP.IPv6Error -> String
showerror_ipv6 (IP.InvalidCharacter) = "InvalidCharacter" showerror_ipv6 (IP.InvalidCharacter) = "InvalidCharacter"
showerror_ipv6 (IP.TooManyHexaDecimalCharacters) = "TooManyHexaDecimalCharacters" showerror_ipv6 (IP.TooManyHexaDecimalCharacters) = "TooManyHexaDecimalCharacters"
showerror_ipv6 (IP.NotEnoughChunks) = "NotEnoughChunks"
showerror_ipv6 (IP.TooManyChunks) = "TooManyChunks"
test_ipv6 :: String -> Effect Unit test_ipv6 :: String -> Effect Unit
test_ipv6 ipv6string = do test_ipv6 ipv6string = do
@ -104,3 +106,4 @@ main = do
log "" log ""
test_ipv6 "2001:0" test_ipv6 "2001:0"
test_ipv6 "2001:0db8:0000:0000:0000:8a2e:0370:7334" test_ipv6 "2001:0db8:0000:0000:0000:8a2e:0370:7334"
test_ipv6 "2001:0db8::8a2e:0370:7334"