cacophony-0.7.0: examples/echo-server/Parse.hs
{-# LANGUAGE OverloadedStrings, GADTs #-}
module Parse where
import Data.Attoparsec.ByteString (Parser, IResult(..), satisfy, anyWord8, take, parseWith)
import Data.Bits ((.|.), shiftL)
import Data.ByteString (ByteString, pack)
import Data.IORef
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
import Data.Word (Word8)
import Network.Simple.TCP (Socket, recv)
import Prelude hiding (take)
import Types
serializeHeader :: Header
-> ByteString
serializeHeader (psk, hp, ct, dt, ht) =
pack [pskByte psk, hpByte hp, ctByte ct, dtByte dt, htByte ht]
where
pskByte False = 0
pskByte True = 1
hpByte NoiseNN = 0
hpByte NoiseKN = 1
hpByte NoiseNK = 2
hpByte NoiseKK = 3
hpByte NoiseNX = 4
hpByte NoiseKX = 5
hpByte NoiseXN = 6
hpByte NoiseIN = 7
hpByte NoiseXK = 8
hpByte NoiseIK = 9
hpByte NoiseXX = 10
hpByte NoiseIX = 11
ctByte :: SomeCipherType -> Word8
ctByte (WrapCipherType CTChaChaPoly1305) = 0
ctByte (WrapCipherType CTAESGCM) = 1
dtByte :: SomeDHType -> Word8
dtByte (WrapDHType DTCurve25519) = 0
dtByte (WrapDHType DTCurve448) = 1
htByte :: SomeHashType -> Word8
htByte (WrapHashType HTSHA256) = 0
htByte (WrapHashType HTSHA512) = 1
htByte (WrapHashType HTBLAKE2s) = 2
htByte (WrapHashType HTBLAKE2b) = 3
pskByteToBool :: Word8 -> Bool
pskByteToBool 0 = False
pskByteToBool 1 = True
pskByteToBool _ = error "invalid PSK setting"
handshakeByteToType :: Word8 -> HandshakeType
handshakeByteToType 0 = NoiseNN
handshakeByteToType 1 = NoiseKN
handshakeByteToType 2 = NoiseNK
handshakeByteToType 3 = NoiseKK
handshakeByteToType 4 = NoiseNX
handshakeByteToType 5 = NoiseKX
handshakeByteToType 6 = NoiseXN
handshakeByteToType 7 = NoiseIN
handshakeByteToType 8 = NoiseXK
handshakeByteToType 9 = NoiseIK
handshakeByteToType 10 = NoiseXX
handshakeByteToType 11 = NoiseIX
handshakeByteToType _ = error "invalid handshake type"
cipherByteToType :: Word8 -> SomeCipherType
cipherByteToType 0 = WrapCipherType CTChaChaPoly1305
cipherByteToType 1 = WrapCipherType CTAESGCM
cipherByteToType _ = error "invalid cipher type"
curveByteToType :: Word8 -> SomeDHType
curveByteToType 0 = WrapDHType DTCurve25519
curveByteToType 1 = WrapDHType DTCurve448
curveByteToType _ = error "invalid curve type"
hashByteToType :: Word8 -> SomeHashType
hashByteToType 0 = WrapHashType HTSHA256
hashByteToType 1 = WrapHashType HTSHA512
hashByteToType 2 = WrapHashType HTBLAKE2s
hashByteToType 3 = WrapHashType HTBLAKE2b
hashByteToType _ = error "invalid hash type"
headerParser :: Parser Header
headerParser = do
psk <- satisfy (< 2)
hsb <- satisfy (< 13)
cib <- satisfy (< 2)
cub <- satisfy (< 2)
hb <- satisfy (< 4)
return (pskByteToBool psk, handshakeByteToType hsb, cipherByteToType cib, curveByteToType cub, hashByteToType hb)
messageParser :: Parser ByteString
messageParser = do
l0 <- fromIntegral <$> anyWord8
l1 <- fromIntegral <$> anyWord8
take (l0 `shiftL` 8 .|. l1)
parseSocket :: IORef ByteString
-> Socket
-> Parser a
-> IO (Maybe a)
parseSocket bufRef sock p = do
buf <- readIORef bufRef
result <- parseWith doRead p buf
case result of
Fail{} -> return Nothing
(Partial _) -> return Nothing
(Done i r) -> modifyIORef' bufRef (const i) >> return (Just r)
where
doRead = do
d <- fromMaybe "" <$> recv sock 2048
modifyIORef' bufRef (<> d)
return d