packages feed

second-transfer-0.10.0.1: hs-src/SecondTransfer/Socks5/Parsers.hs

{-# LANGUAGE TemplateHaskell, OverloadedStrings, GeneralizedNewtypeDeriving  #-}
module SecondTransfer.Socks5.Parsers (
                 parseProtocolVersion
               , parseAddressType
               , parseProtocolCommand
               , parseReplyField
               , parseClientAuthMethods_Packet
               , parseServerSelectsMethod_Packet
               , parseIndicatedAddress
               , parseClientRequest_Packet
     ) where

import           Control.Applicative                                ( (<|>) )

import           Data.Word
import           Data.Bits
import qualified Data.ByteString                                    as B
import qualified Data.Attoparsec.ByteString                         as P

import           SecondTransfer.Socks5.Types


parseProtocolVersion :: P.Parser ProtocolVersion
parseProtocolVersion = P.word8 5 >> pure  ProtocolVersion


parseAddressType :: P.Parser AddressType
parseAddressType =
       ( P.word8 1 >> pure IPv4_S5AT)
  <|>  ( P.word8 3 >> pure DomainName_S5AT)
  <|>  ( P.word8 4 >> pure IPv6_S5AT)


parseProtocolCommand :: P.Parser ProtocolCommand
parseProtocolCommand =
       ( P.word8 1 >> pure Connect_S5PC )
  <|>  ( P.word8 2 >> pure Bind_S5PC )
  <|>  ( P.word8 3 >> pure UdpAssociate_S5PC )


parseReplyField :: P.Parser ReplyField
parseReplyField =
       ( P.word8 0 >> pure Succeeded_S5RF )
  <|>  ( P.word8 1 >> pure GeneralFailure_S5RF )


parseClientAuthMethods_Packet :: P.Parser ClientAuthMethods_Packet
parseClientAuthMethods_Packet = ClientAuthMethods_Packet <$>
       parseProtocolVersion
  <*>  ( P.anyWord8 >>= \ len -> P.take . fromIntegral $ len)


parseServerSelectsMethod_Packet :: P.Parser ServerSelectsMethod_Packet
parseServerSelectsMethod_Packet = error "YetTooImplement(ButItsSuperEasy)"


parseIndicatedAddress :: AddressType -> P.Parser IndicatedAddress
parseIndicatedAddress IPv4_S5AT =
       IPv4_IA
  <$>  anyWord32be
parseIndicatedAddress IPv6_S5AT =
       IPv6_IA
  <$>  P.take 16
parseIndicatedAddress DomainName_S5AT =
       DomainName_IA
  <$>  (P.anyWord8 >>= \ len -> P.take . fromIntegral $ len)


parseClientRequest_Packet :: P.Parser ClientRequest_Packet
parseClientRequest_Packet =
       ClientRequest_Packet
  <$>  parseProtocolVersion
  <*>  parseProtocolCommand
  <*>  (P.word8 0)  -- Reserved part
  <*>  (parseAddressType >>= parseIndicatedAddress )
  <*>  anyWord16be

-- Some definitions copy-pasted from Data.Attoparsec.Binary ... Original code by Andrew Drake,
-- all rights reserved by him, used under BSD3 license.

byteSize :: (FiniteBits a) => a -> Int
byteSize = (`div` 8) . finiteBitSize

pack :: (FiniteBits a, Num a) => B.ByteString -> a
pack = B.foldl' (\n h -> (n `shiftL` 8) .|. fromIntegral h) 0

anyWordN :: (FiniteBits a) => (B.ByteString -> a) -> P.Parser a
anyWordN = anyWordN' undefined
  where anyWordN' :: (FiniteBits a) => a -> (B.ByteString -> a) -> P.Parser a
        anyWordN' d = flip fmap $ P.take $ byteSize d

-- | Match any 16 bit big endian word.
anyWord16be :: P.Parser Word16
anyWord16be = anyWordN pack


-- | Match any 32 bit big endian word.
anyWord32be :: P.Parser Word32
anyWord32be = anyWordN pack


unpack :: (FiniteBits a, Integral a) => a -> B.ByteString
unpack x = B.pack $ map f $ reverse [0..byteSize x - 1]
   where f s = fromIntegral $ shiftR x (8 * s)

wordN :: (FiniteBits a) => (a -> B.ByteString) -> a -> P.Parser a
wordN u w = P.string (u w) >> return w

-- | Match a specific 16-bit big-endian word.
word16be :: Word16 -> P.Parser Word16
word16be = wordN unpack