{- | Module : Network.Socks5.Wire License : BSD-style Copyright : (c) 2010-2019 Vincent Hanquez Stability : experimental Portability : unknown -} module Network.Socks5.Wire ( SocksHello (..) , SocksHelloResponse (..) , SocksRequest (..) , SocksResponse (..) ) where import Control.Monad ( liftM4, replicateM ) import qualified Data.ByteString as B import Data.Serialize ( Get, Put, PutM (..), Serialize (..), getByteString , getWord16be, getWord32host, getWord8, putByteString , putWord16be, putWord32host, putWord8 ) import Network.Socket ( PortNumber ) import Network.Socks5.Types ( SocksCommand, SocksHostAddress (..), SocksMethod , SocksReply ) -- | Type representing initial messages sent by a client with the list of -- authentification methods supported. newtype SocksHello = SocksHello { getSocksHelloMethods :: [SocksMethod] } deriving (Eq, Show) -- | Type representing initial messages sent by a server in response to Hello, -- with the server's chosen method of authentication. newtype SocksHelloResponse = SocksHelloResponse { getSocksHelloResponseMethod :: SocksMethod } deriving (Eq, Show) -- | Type representing SOCKS requests. data SocksRequest = SocksRequest { requestCommand :: SocksCommand , requestDstAddr :: SocksHostAddress , requestDstPort :: PortNumber } deriving (Eq, Show) -- | Type representing SOCKS responses. data SocksResponse = SocksResponse { responseReply :: SocksReply , responseBindAddr :: SocksHostAddress , responseBindPort :: PortNumber } deriving (Eq, Show) getAddr :: (Eq a, Num a, Show a) => a -> Get SocksHostAddress getAddr 1 = SocksAddrIPV4 <$> getWord32host getAddr 3 = SocksAddrDomainName <$> (getLength8 >>= getByteString) getAddr 4 = SocksAddrIPV6 <$> liftM4 (,,,) getWord32host getWord32host getWord32host getWord32host getAddr n = error ("cannot get unknown socket address type: " <> show n) putAddr :: SocksHostAddress -> PutM () putAddr (SocksAddrIPV4 h) = putWord8 1 >> putWord32host h putAddr (SocksAddrDomainName b) = putWord8 3 >> putLength8 (B.length b) >> putByteString b putAddr (SocksAddrIPV6 (a, b, c, d)) = putWord8 4 >> mapM_ putWord32host [a,b,c,d] putEnum8 :: Enum e => e -> Put putEnum8 = putWord8 . fromIntegral . fromEnum getEnum8 :: Enum e => Get e getEnum8 = toEnum . fromIntegral <$> getWord8 putLength8 :: Int -> Put putLength8 = putWord8 . fromIntegral getLength8 :: Get Int getLength8 = fromIntegral <$> getWord8 getSocksRequest :: (Eq a, Num a, Show a) => a -> Get SocksRequest getSocksRequest 5 = do cmd <- getEnum8 _ <- getWord8 addr <- getWord8 >>= getAddr port <- fromIntegral <$> getWord16be return $ SocksRequest cmd addr port getSocksRequest v = error ("unsupported version of the protocol " <> show v) getSocksResponse :: (Eq a, Num a, Show a) => a -> Get SocksResponse getSocksResponse 5 = do reply <- getEnum8 _ <- getWord8 addr <- getWord8 >>= getAddr port <- fromIntegral <$> getWord16be return $ SocksResponse reply addr port getSocksResponse v = error ("unsupported version of the protocol " <> show v) instance Serialize SocksHello where put (SocksHello ms) = do putWord8 5 putLength8 (length ms) mapM_ putEnum8 ms get = do v <- getWord8 case v of 5 -> SocksHello <$> (getLength8 >>= flip replicateM getEnum8) _ -> error "unsupported sock hello version" instance Serialize SocksHelloResponse where put (SocksHelloResponse m) = putWord8 5 >> putEnum8 m get = do v <- getWord8 case v of 5 -> SocksHelloResponse <$> getEnum8 _ -> error "unsupported sock hello response version" instance Serialize SocksRequest where put req = do putWord8 5 putEnum8 $ requestCommand req putWord8 0 putAddr $ requestDstAddr req putWord16be $ fromIntegral $ requestDstPort req get = getWord8 >>= getSocksRequest instance Serialize SocksResponse where put req = do putWord8 5 putEnum8 $ responseReply req putWord8 0 putAddr $ responseBindAddr req putWord16be $ fromIntegral $ responseBindPort req get = getWord8 >>= getSocksResponse