moesocks-1.0.0.0: src/Network/MoeSocks/BuilderAndParser.hs
{-# LANGUAGE OverloadedStrings #-}
module Network.MoeSocks.BuilderAndParser where
import Control.Lens
import Data.Attoparsec.ByteString
import Data.Binary
import Data.Binary.Put
import Data.Maybe
import Data.Monoid
import Data.Text.Lens
import Data.Text.Strict.Lens (utf8)
import Network.MoeSocks.Helper
import Network.MoeSocks.Type
import Network.Socket
import Prelude hiding ((-), take)
import qualified Data.ByteString as S
import qualified Data.ByteString.Builder as B
import qualified Prelude as P
_No_authentication :: Word8
_No_authentication = 0
_Request_Granted :: Word8
_Request_Granted = 0
_ReservedByte :: Word8
_ReservedByte = 0
_SpaceCode :: Word8
_SpaceCode = 32
socksVersion :: Word8
socksVersion = 5
-- Builder
greetingReplyBuilder :: B.Builder
greetingReplyBuilder = B.word8 socksVersion
<> B.word8 _No_authentication
sockAddr_To_Pair :: SockAddr -> (AddressType, Port)
sockAddr_To_Pair aSockAddr = case aSockAddr of
SockAddrInet _port _host ->
let
_r@(_a, _b, _c, _d) =
decode . runPut . put - _host
:: (Word8, Word8, Word8, Word8)
in
( IPv4_Address - flip4 _r
, fromIntegral _port
)
SockAddrInet6 _port _ _host _ ->
let
_r@(_a, _b, _c, _d, _e, _f, _g, _h) =
decode . runPut . put - _host
:: (Word16, Word16, Word16, Word16
, Word16, Word16, Word16, Word16)
in
( IPv6_Address - _r ^.. each
, fromIntegral _port
)
SockAddrUnix x ->
let
_host = P.takeWhile (/= ':') x
_port = x & reverse
& P.takeWhile (/= ':')
& reverse
in
( DomainName - (_host & review _Text)
, fromMaybe 0 - _port ^? _Show
)
x ->
error - "SockAddrCan not implemented: "
<> show x
portBuilder :: (Integral i) => i -> B.Builder
portBuilder i =
let _i = fromIntegral i :: Word16
in
foldMapOf both B.word8 -
(decode - runPut - put _i :: (Word8, Word8))
connectionReplyBuilder :: SockAddr -> B.Builder
connectionReplyBuilder aSockAddr =
let _r@(__addressType, _port) = sockAddr_To_Pair aSockAddr
in
B.word8 socksVersion
<> B.word8 _Request_Granted
<> B.word8 _ReservedByte
<> addressTypeBuilder __addressType
<> portBuilder _port
addressTypeBuilder :: AddressType -> B.Builder
addressTypeBuilder aAddressType =
case aAddressType of
IPv4_Address _address ->
B.word8 1
<> foldMapOf each B.word8 _address
DomainName x ->
B.word8 3
<> B.word8 (fromIntegral (S.length (review utf8 x)))
<> B.byteString (review utf8 x)
IPv6_Address _address ->
B.word8 4
<> foldMapOf each B.word16BE _address
connectionType_To_Word8 :: ConnectionType -> Word8
connectionType_To_Word8 TCP_IP_StreamConnection = 1
{-connectionType_To_Word8 TCP_IP_PortBinding = 2-}
connectionType_To_Word8 UDP_Port = 3
requestBuilder :: ClientRequest -> B.Builder
requestBuilder aClientRequest =
B.word8 (connectionType_To_Word8 - aClientRequest ^. connectionType)
<> B.word8 _ReservedByte
<> addressTypeBuilder (aClientRequest ^. addressType)
<> portBuilder (aClientRequest ^. portNumber)
shadowSocksRequestBuilder :: ClientRequest -> B.Builder
shadowSocksRequestBuilder aClientRequest =
addressTypeBuilder (aClientRequest ^. addressType)
<> portBuilder (aClientRequest ^. portNumber)
-- Parser
socksHeader :: Parser Word8
socksHeader = word8 socksVersion
greetingParser :: Parser ClientGreeting
greetingParser = do
socksHeader
let maxNoOfMethods = 5
_numberOfAuthenticationMethods <- satisfy (<= maxNoOfMethods)
ClientGreeting <$>
count (fromIntegral _numberOfAuthenticationMethods) anyWord8
portParser :: Parser Int
portParser = do
__portNumberPair <- (,) <$> anyWord8 <*> anyWord8
pure - portPairToInt __portNumberPair
requestParser :: Parser ClientRequest
requestParser = do
__connectionType <- choice
[
TCP_IP_StreamConnection <$ word8 1
{-, TCP_IP_PortBinding <$ word8 2-}
, UDP_Port <$ word8 3
]
word8 _ReservedByte
__addressType <- addressTypeParser
__portNumber <- portParser
pure -
ClientRequest
__connectionType
__addressType
__portNumber
connectionParser :: Parser ClientRequest
connectionParser = do
socksHeader
requestParser
anyWord16 :: Parser Word16
anyWord16 = do
_b <- (,) <$> anyWord8 <*> anyWord8
pure - decode - runPut - put _b
addressTypeParser :: Parser AddressType
addressTypeParser = choice
[
IPv4_Address <$> do
word8 1
_a <- anyWord8
_b <- anyWord8
_c <- anyWord8
_d <- anyWord8
pure - (_a, _b, _c, _d)
, DomainName <$> do
word8 3
_nameLength <- anyWord8
view utf8 <$> (take - fromIntegral _nameLength)
, IPv6_Address <$> do
word8 4
_r <- count 8 anyWord16
{-pure - trace ("parsed IPv6: " <> show _r) _r-}
pure _r
]
shadowSocksRequestParser :: ConnectionType -> Parser ClientRequest
shadowSocksRequestParser _connectionType = do
_addressType <- addressTypeParser
_portNumber <- portParser
pure -
ClientRequest
_connectionType
_addressType
_portNumber