packages feed

moesocks-0.1.0.4: 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.Monoid
import Data.Maybe
import Data.Text.Lens
import Data.Text.Strict.Lens (utf8)
import Safe (readMay)
import Network.MoeSocks.Constant
import Network.MoeSocks.Helper
import Network.MoeSocks.Type
import Prelude hiding ((-), take)
import qualified Prelude as P
import qualified Data.ByteString as S
import qualified Data.ByteString.Builder as B
import Network.Socket

socksVersion :: Word8
socksVersion = 5

socksHeader :: Parser Word8
socksHeader = word8 socksVersion

greetingParser :: Parser ClientGreeting
greetingParser = do
  socksHeader
  let maxNoOfMethods = 5
  _numberOfAuthenticationMethods <- satisfy (<= maxNoOfMethods)

  ClientGreeting <$>
    count (fromIntegral _numberOfAuthenticationMethods) anyWord8

greetingReplyBuilder :: B.Builder
greetingReplyBuilder =  B.word8 socksVersion
                     <> B.word8 _No_authentication

portParser :: Parser Int
portParser = do
  __portNumberPair <- (,) <$> anyWord8 <*> anyWord8
  pure - portPairToInt __portNumberPair

portBuilder :: (Integral i) => i -> B.Builder
portBuilder i =
  let _i = fromIntegral i :: Word16 
  in
  foldMapOf both B.word8 -
    (decode - runPut - put _i :: (Word8, Word8))

requestParser :: Parser ClientRequest
requestParser = do
  __connectionType <- choice
      [
        TCP_IP_stream_connection <$ word8 1 
      , TCP_IP_port_binding <$ word8 2
      , UDP_port <$ word8 3
      ]

  word8 _ReservedByte
  __addressType <- addressTypeParser
  __portNumber <- portParser
  pure - 
          ClientRequest
            __connectionType
            __addressType 
            __portNumber

connectionParser :: Parser ClientRequest 
connectionParser = do
  socksHeader
  requestParser

sockAddr_To_Pair :: SockAddr -> (AddressType, Port)
sockAddr_To_Pair (SockAddrInet _port _host) =
                        let 
                            _r@(_a, _b, _c, _d) = decode . 
                                    runPut - put - _host
                                      :: (Word8, Word8, Word8, Word8)
                        in
                        ( IPv4_address - flip4 _r
                        , fromIntegral _port
                        )
sockAddr_To_Pair (SockAddrInet6 _port _ _host _) =
                        let 
                            _r@(_a, _b, _c, _d) = decode . 
                                    runPut - put - _host
                                      :: (Word32, Word32, Word32, Word32)
                        in
                        ( IPv6_address - flip4 _r
                        , fromIntegral _port
                        )
sockAddr_To_Pair (SockAddrUnix x) = 
                        let
                            _host = P.takeWhile (/= ':') x :: String 
                            _port = x & reverse & P.takeWhile (/= ':') 
                                      & reverse
                        in
                        ( Domain_name - (_host & review _Text)
                        , fromMaybe 0 - readMay _port
                        )
sockAddr_To_Pair sa = error - "SockAddrCan not implemented: " <> show sa 


connectionReplyBuilder :: SockAddr -> B.Builder
connectionReplyBuilder aSockAddr = 
  let _r@(__addressType, _port) = sockAddr_To_Pair aSockAddr
  in


  let _b =
              B.word8 socksVersion
          <>  B.word8 _Request_Granted 
          <>  B.word8 _ReservedByte
          <>  addressTypeBuilder __addressType
          <>  portBuilder _port
  in
  _b

addressTypeBuilder :: AddressType -> B.Builder
addressTypeBuilder aAddressType = 
  case aAddressType of
    IPv4_address _address -> 
                          B.word8 1
                       <> foldMapOf each B.word8 _address
    Domain_name 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.word32BE _address



connectionType_To_Word8 :: ConnectionType -> Word8
connectionType_To_Word8 TCP_IP_stream_connection = 1
connectionType_To_Word8 TCP_IP_port_binding = 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)

anyWord32be :: Parser Word32
anyWord32be = do
  _b <- count 4 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)
  
  , Domain_name <$>   do 
                        word8 3
                        let maxDomainNameLength = 32
                        _nameLength <- satisfy (<= maxDomainNameLength)
                        view utf8 <$> (take - fromIntegral _nameLength)

  , IPv6_address <$>  do
                        word8 4 
                        _a <- anyWord32be
                        _b <- anyWord32be
                        _c <- anyWord32be
                        _d <- anyWord32be 
                        pure - (_a, _b, _c, _d)
  ]


shadowsocksRequestParser :: Parser ClientRequest
shadowsocksRequestParser = do
  __addressType <- addressTypeParser
  __portNumber <- portParser

  pure - 
          ClientRequest
            TCP_IP_stream_connection
            __addressType 
            __portNumber