socks 0.4.2 → 0.5.0
raw patch · 7 files changed
+387/−239 lines, 7 files
Files
- Network/Socks5.hs +81/−45
- Network/Socks5/Command.hs +73/−51
- Network/Socks5/Conf.hs +43/−0
- Network/Socks5/Lowlevel.hs +29/−0
- Network/Socks5/Types.hs +95/−83
- Network/Socks5/Wire.hs +62/−58
- socks.cabal +4/−2
Network/Socks5.hs view
@@ -7,42 +7,76 @@ -- Stability : experimental -- Portability : unknown --+-- This is an implementation of SOCKS5 as defined in RFC 1928+--+-- In Wikipedia's words:+--+-- SOCKet Secure (SOCKS) is an Internet protocol that routes network packets+-- between a client and server through a proxy server. SOCKS5 additionally+-- provides authentication so only authorized users may access a server.+-- Practically, a SOCKS server will proxy TCP connections to an arbitrary IP+-- address as well as providing a means for UDP packets to be forwarded.+--+-- BIND and UDP ASSOCIATE messages are not implemented.+-- However main usage of SOCKS is covered in this implementation.+-- module Network.Socks5- ( SocksConf(..)- , defaultSocksConf- , socksConnectAddr- , socksConnectName- , socksConnectTo- , socksConnectWith- ) where+ (+ -- * Types+ SocksAddress(..)+ , SocksHostAddress(..)+ , SocksReply(..)+ , SocksError(..)+ -- * Configuration+ , module Network.Socks5.Conf+ -- * Methods+ , socksConnectWithSocket+ , socksConnect+ -- * Variants+ , socksConnectAddr+ , socksConnectName+ , socksConnectTo+ , socksConnectWith+ ) where import Control.Monad import Control.Exception+import qualified Data.ByteString.Char8 as BC import Network.Socket import Network.BSD-import Network.Socks5.Command+import qualified Network.Socks5.Command as Cmd+import Network.Socks5.Conf import Network.Socks5.Types+import Network.Socks5.Lowlevel import Network import System.IO --- | SOCKS configuration structure.--- this structure will be extended in future to support authentification.--- use defaultSocksConf to create new record.-data SocksConf = SocksConf- { socksHost :: String -- ^ SOCKS host.- , socksPort :: PortNumber -- ^ SOCKS port.- , socksVersion :: Int -- ^ SOCKS version to use, only 5 supported for now.- }---- | defaultSocksConf create a new record, making sure--- API remains compatible when the record is extended.-defaultSocksConf host port = SocksConf host port 5+-- | connect a user specified new socket to the socks server,+-- and connect the stream on the server side to the 'SockAddress' specified.+--+-- |socket|-----sockServer----->|server|----destAddr----->|destination|+--+socksConnectWithSocket :: Socket -- ^ Socket to use.+ -> SocksConf -- ^ SOCKS configuration for the server.+ -> SocksAddress -- ^ SOCKS Address to connect to.+ -> IO (SocksHostAddress, PortNumber)+socksConnectWithSocket sock serverConf destAddr = do+ serverAddr <- resolveToSockAddr (socksServer serverConf)+ connect sock serverAddr+ r <- Cmd.establish sock [SocksMethodNone]+ when (r == SocksMethodNotAcceptable) $ error "cannot connect with no socks method of authentication"+ Cmd.rpc_ sock (Connect destAddr) -withSocks sock sockaddr f = do- connect sock sockaddr- r <- socks5Establish sock [SocksMethodNone]- when (r == SocksMethodNotAcceptable) $ error "cannot connect with no socks method of authentication"- f+-- | connect a new socket to a socks server and connect the stream on the+-- server side to the 'SocksAddress' specified.+socksConnect :: SocksConf -- ^ SOCKS configuration for the server.+ -> SocksAddress -- ^ SOCKS Address to connect to.+ -> IO (Socket, (SocksHostAddress, PortNumber))+socksConnect serverConf destAddr =+ getProtocolNumber "tcp" >>= \proto ->+ bracketOnError (socket AF_INET Stream proto) sClose $ \sock -> do+ ret <- socksConnectWithSocket sock serverConf destAddr+ return (sock, ret) -- | connect a new socket to the socks server, and connect the stream on the server side -- to the sockaddr specified. the sockaddr need to be SockAddrInet or SockAddrInet6.@@ -50,19 +84,22 @@ -- a unix sockaddr will raises an exception. -- -- |socket|-----sockServer----->|server|----destAddr----->|destination|+{-# DEPRECATED socksConnectAddr "use socksConnectWithSocket" #-} socksConnectAddr :: Socket -> SockAddr -> SockAddr -> IO ()-socksConnectAddr sock sockserver destaddr = withSocks sock sockserver $ do- case destaddr of- SockAddrInet p h -> socks5ConnectIPV4 sock h p >> return ()- SockAddrInet6 p _ h _ -> socks5ConnectIPV6 sock h p >> return ()- _ -> error "unsupported unix sockaddr type"+socksConnectAddr sock sockserver destaddr =+ socksConnectWithSocket sock+ (defaultSocksConfFromSockAddr sockserver)+ (socksServer $ defaultSocksConfFromSockAddr destaddr) >>+ return () -- | connect a new socket to the socks server, and connect the stream to a FQDN -- resolved on the server side. socksConnectName :: Socket -> SockAddr -> String -> PortNumber -> IO ()-socksConnectName sock sockserver destination port = withSocks sock sockserver $ do- _ <- socks5ConnectDomainName sock destination port- return ()+socksConnectName sock sockserver destination port = do+ socksConnectWithSocket sock+ (defaultSocksConfFromSockAddr sockserver)+ (SocksAddress (SocksAddrDomainName $ BC.pack destination) port)+ >> return () -- | create a new socket and connect in to a destination through the specified -- SOCKS configuration.@@ -70,22 +107,21 @@ -> String -- ^ destination hostname -> PortID -- ^ destination port -> IO Socket-socksConnectWith sockConf desthost destport = do- dport <- resolvePortID destport- proto <- getProtocolNumber "tcp"- bracketOnError (socket AF_INET Stream proto) sClose $ \sock -> do- he <- getHostByName $ socksHost sockConf- let sockaddr = SockAddrInet (socksPort sockConf) (hostAddress he)- socksConnectName sock sockaddr desthost dport- return sock+socksConnectWith socksConf desthost destport = do+ dport <- resolvePortID destport+ proto <- getProtocolNumber "tcp"+ bracketOnError (socket AF_INET Stream proto) sClose $ \sock -> do+ sockaddr <- resolveToSockAddr (socksServer socksConf)+ socksConnectName sock sockaddr desthost dport+ return sock -- | similar to Network connectTo but use a socks proxy with default socks configuration. socksConnectTo :: String -> PortID -> String -> PortID -> IO Handle socksConnectTo sockshost socksport desthost destport = do- sport <- resolvePortID socksport- let socksConf = defaultSocksConf sockshost sport- sock <- socksConnectWith socksConf desthost destport- socketToHandle sock ReadWriteMode+ sport <- resolvePortID socksport+ let socksConf = defaultSocksConf sockshost sport+ sock <- socksConnectWith socksConf desthost destport+ socketToHandle sock ReadWriteMode resolvePortID (Service serv) = getServicePortNumber serv resolvePortID (PortNumber n) = return n
Network/Socks5/Command.hs view
@@ -8,13 +8,18 @@ -- Portability : unknown -- module Network.Socks5.Command- ( socks5Establish- , socks5ConnectIPV4- , socks5ConnectIPV6- , socks5ConnectDomainName- -- * lowlevel interface- , socks5Rpc- ) where+ ( establish+ , Connect(..)+ , Command(..)+ , connectIPV4+ , connectIPV6+ , connectDomainName+ -- * lowlevel interface+ , rpc+ , rpc_+ , sendSerialized+ , waitSerialized+ ) where import Control.Applicative import Control.Exception@@ -29,56 +34,73 @@ import Network.Socks5.Types import Network.Socks5.Wire -socks5Establish :: Socket -> [SocksMethod] -> IO SocksMethod-socks5Establish socket methods = do- sendAll socket (encode $ SocksHello methods)- getSocksHelloResponseMethod <$> runGetDone get (recv socket 4096)+establish :: Socket -> [SocksMethod] -> IO SocksMethod+establish socket methods = do+ sendAll socket (encode $ SocksHello methods)+ getSocksHelloResponseMethod <$> runGetDone get (recv socket 4096) -socks5ConnectIPV4 :: Socket -> HostAddress -> PortNumber -> IO (HostAddress, PortNumber)-socks5ConnectIPV4 socket hostaddr port = onReply <$> socks5Rpc socket request- where- request = SocksRequest- { requestCommand = SocksCommandConnect- , requestDstAddr = SocksAddrIPV4 hostaddr- , requestDstPort = fromIntegral port- }+newtype Connect = Connect SocksAddress deriving (Show,Eq) - onReply (SocksAddrIPV4 h, p) = (h, p)- onReply _ = error "ipv4 requested, got something different"+class Command a where+ toRequest :: a -> SocksRequest+ fromRequest :: SocksRequest -> Maybe a -socks5ConnectIPV6 :: Socket -> HostAddress6 -> PortNumber -> IO (HostAddress6, PortNumber)-socks5ConnectIPV6 socket hostaddr6 port = onReply <$> socks5Rpc socket request- where- request = SocksRequest- { requestCommand = SocksCommandConnect- , requestDstAddr = SocksAddrIPV6 hostaddr6- , requestDstPort = fromIntegral port- }- onReply (SocksAddrIPV6 h, p) = (h, p)- onReply _ = error "ipv6 requested, got something different"+instance Command SocksRequest where+ toRequest = id+ fromRequest = Just +instance Command Connect where+ toRequest (Connect (SocksAddress ha port)) = SocksRequest+ { requestCommand = SocksCommandConnect+ , requestDstAddr = ha+ , requestDstPort = fromIntegral port+ }+ fromRequest req+ | requestCommand req /= SocksCommandConnect = Nothing+ | otherwise = Just $ Connect $ SocksAddress (requestDstAddr req) (requestDstPort req)++connectIPV4 :: Socket -> HostAddress -> PortNumber -> IO (HostAddress, PortNumber)+connectIPV4 socket hostaddr port = onReply <$> rpc_ socket (Connect $ SocksAddress (SocksAddrIPV4 hostaddr) port)+ where onReply (SocksAddrIPV4 h, p) = (h, p)+ onReply _ = error "ipv4 requested, got something different"++connectIPV6 :: Socket -> HostAddress6 -> PortNumber -> IO (HostAddress6, PortNumber)+connectIPV6 socket hostaddr6 port = onReply <$> rpc_ socket (Connect $ SocksAddress (SocksAddrIPV6 hostaddr6) port)+ where onReply (SocksAddrIPV6 h, p) = (h, p)+ onReply _ = error "ipv6 requested, got something different"+ -- TODO: FQDN should only be ascii, maybe putting a "fqdn" data type -- in front to make sure and make the BC.pack safe.-socks5ConnectDomainName :: Socket -> String -> PortNumber -> IO (SocksAddr, PortNumber)-socks5ConnectDomainName socket fqdn port = socks5Rpc socket $ SocksRequest- { requestCommand = SocksCommandConnect- , requestDstAddr = SocksAddrDomainName $ BC.pack fqdn- , requestDstPort = fromIntegral port- }+connectDomainName :: Socket -> String -> PortNumber -> IO (SocksHostAddress, PortNumber)+connectDomainName socket fqdn port = rpc_ socket $ Connect $ SocksAddress (SocksAddrDomainName $ BC.pack fqdn) port -socks5Rpc :: Socket -> SocksRequest -> IO (SocksAddr, PortNumber)-socks5Rpc socket req = do- sendAll socket (encode req)- onReply <$> runGetDone get (recv socket 4096)- where onReply res@(responseReply -> reply)- | reply /= SocksReplySuccess = throw $ SocksError reply- | otherwise =- (responseBindAddr res, fromIntegral $ responseBindPort res)+sendSerialized :: Serialize a => Socket -> a -> IO ()+sendSerialized sock a = sendAll sock $ encode a -runGetDone :: Show a => Get a -> IO ByteString -> IO a+waitSerialized :: Serialize a => Socket -> IO a+waitSerialized sock = runGetDone get (getMore sock)++rpc :: Command a => Socket -> a -> IO (Either SocksError (SocksHostAddress, PortNumber))+rpc socket req = do+ sendSerialized socket (toRequest req)+ onReply <$> runGetDone get (getMore socket)+ where onReply res@(responseReply -> reply) =+ case reply of+ SocksReplySuccess -> Right (responseBindAddr res, fromIntegral $ responseBindPort res)+ SocksReplyError e -> Left e++rpc_ :: Command a => Socket -> a -> IO (SocksHostAddress, PortNumber)+rpc_ socket req = rpc socket req >>= either throwIO return++-- this function expect all the data to be consumed. this is fine for intertwined message,+-- but might not be a good idea for multi messages from one party.+runGetDone :: Serialize a => Get a -> IO ByteString -> IO a runGetDone getter ioget = ioget >>= return . runGetPartial getter >>= r where- r (Fail s) = error s- r (Partial cont) = ioget >>= r . cont- r (Done a b)- | not $ B.null b = error "got too many bytes while receiving data"- | otherwise = return a+ r (Fail s) = error s+ r (Partial cont) = ioget >>= r . cont+ r (Done a b)+ | not $ B.null b = error "got too many bytes while receiving data"+ | otherwise = return a++getMore :: Socket -> IO ByteString+getMore socket = recv socket 4096
+ Network/Socks5/Conf.hs view
@@ -0,0 +1,43 @@+module Network.Socks5.Conf+ ( SocksConf(..)+ , socksHost+ , socksPort+ , defaultSocksConf + , defaultSocksConfFromSockAddr+ ) where++import Network.Socket+import Network.Socks5.Types (SocksAddress(..), SocksHostAddress(..), SocksVersion(..))+import qualified Data.ByteString.Char8 as BC++-- | SOCKS configuration structure.+-- this structure will be extended in future to support authentification.+-- use defaultSocksConf to create new record.+data SocksConf = SocksConf+ { socksServer :: SocksAddress -- ^ SOCKS Address+ , socksVersion :: SocksVersion -- ^ SOCKS version to use+ }++-- | SOCKS Host+socksHost :: SocksConf -> SocksHostAddress+socksHost conf = ha where (SocksAddress ha _) = socksServer conf++-- | SOCKS Port+socksPort :: SocksConf -> PortNumber+socksPort conf = port where (SocksAddress _ port) = socksServer conf++-- | defaultSocksConf create a new record, making sure+-- API remains compatible when the record is extended.+defaultSocksConf host port = SocksConf server SocksVer5+ where server = SocksAddress haddr port+ haddr = SocksAddrDomainName $ BC.pack host++-- | same as defaultSocksConf except the server address is determined from a 'SockAddr'+--+-- A unix SockAddr will raises an error. Only Inet and Inet6 types supported+defaultSocksConfFromSockAddr sockaddr = SocksConf server SocksVer5+ where server = SocksAddress haddr port+ (haddr,port) = case sockaddr of+ SockAddrInet p h -> (SocksAddrIPV4 h, p)+ SockAddrInet6 p _ h _ -> (SocksAddrIPV6 h, p)+ _ -> error "unsupported unix sockaddr type"
+ Network/Socks5/Lowlevel.hs view
@@ -0,0 +1,29 @@+module Network.Socks5.Lowlevel+ ( resolveToSockAddr+ , socksListen+ -- * lowlevel types+ , module Network.Socks5.Wire+ , module Network.Socks5.Command+ ) where++import Network.Socket+import Network.BSD+import Network.Socks5.Command+import Network.Socks5.Wire+import Network.Socks5.Types+import qualified Data.ByteString.Char8 as BC++resolveToSockAddr :: SocksAddress -> IO SockAddr+resolveToSockAddr (SocksAddress sockHostAddr port) =+ case sockHostAddr of+ SocksAddrIPV4 ha -> return $ SockAddrInet port ha+ SocksAddrIPV6 ha6 -> return $ SockAddrInet6 port 0 ha6 0+ SocksAddrDomainName bs -> do he <- getHostByName (BC.unpack bs)+ return $ SockAddrInet port (hostAddress he)++socksListen :: Socket -> IO SocksRequest+socksListen sock = do+ hello <- waitSerialized sock+ case getSocksHelloMethods hello of+ _ -> do sendSerialized sock (SocksHelloResponse SocksMethodNone)+ waitSerialized sock
Network/Socks5/Types.hs view
@@ -6,109 +6,121 @@ -- Stability : experimental -- Portability : unknown module Network.Socks5.Types- ( SocksCommand(..)- , SocksMethod(..)- , SocksAddr(..)- , SocksReply(..)- , SocksVersionNotSupported(..)- , SocksError(..)- ) where+ ( SocksVersion(..)+ , SocksCommand(..)+ , SocksMethod(..)+ , SocksHostAddress(..)+ , SocksAddress(..)+ , SocksReply(..)+ , SocksVersionNotSupported(..)+ , SocksError(..)+ ) where import Data.ByteString (ByteString) import Data.Word import Data.Data-import Network.Socket (HostAddress, HostAddress6)+import Network.Socket (HostAddress, HostAddress6, PortNumber) import Control.Exception +-- | Socks Version+data SocksVersion = SocksVer5+ deriving (Show,Eq)+ data SocksCommand =- SocksCommandConnect- | SocksCommandBind- | SocksCommandUdpAssociate- | SocksCommandOther Word8- deriving (Show,Eq,Ord)+ SocksCommandConnect+ | SocksCommandBind+ | SocksCommandUdpAssociate+ | SocksCommandOther Word8+ deriving (Show,Eq,Ord) data SocksMethod =- SocksMethodNone- | SocksMethodGSSAPI- | SocksMethodUsernamePassword- | SocksMethodOther Word8- | SocksMethodNotAcceptable- deriving (Show,Eq,Ord)+ SocksMethodNone+ | SocksMethodGSSAPI+ | SocksMethodUsernamePassword+ | SocksMethodOther Word8+ | SocksMethodNotAcceptable+ deriving (Show,Eq,Ord) -data SocksAddr =- SocksAddrIPV4 HostAddress- | SocksAddrDomainName ByteString- | SocksAddrIPV6 HostAddress6- deriving (Show,Eq)+data SocksHostAddress =+ SocksAddrIPV4 HostAddress+ | SocksAddrDomainName ByteString+ | SocksAddrIPV6 HostAddress6+ deriving (Show,Eq) +data SocksAddress = SocksAddress SocksHostAddress PortNumber+ deriving (Show,Eq)+ data SocksReply =- SocksReplySuccess- | SocksReplyGeneralServerFailure- | SocksReplyConnectionNotAllowedByRule- | SocksReplyNetworkUnreachable- | SocksReplyHostUnreachable- | SocksReplyConnectionRefused- | SocksReplyTTLExpired- | SocksReplyCommandNotSupported- | SocksReplyAddrTypeNotSupported- | SocksReplyOther Word8- deriving (Show,Eq,Ord,Data,Typeable)+ SocksReplySuccess+ | SocksReplyError SocksError+ deriving (Show,Eq,Ord,Data,Typeable) -data SocksVersionNotSupported = SocksVersionNotSupported- deriving (Show,Data,Typeable)+data SocksError =+ SocksErrorGeneralServerFailure+ | SocksErrorConnectionNotAllowedByRule+ | SocksErrorNetworkUnreachable+ | SocksErrorHostUnreachable+ | SocksErrorConnectionRefused+ | SocksErrorTTLExpired+ | SocksErrorCommandNotSupported+ | SocksErrorAddrTypeNotSupported+ | SocksErrorOther Word8+ deriving (Show,Eq,Ord,Data,Typeable) -data SocksError = SocksError SocksReply- deriving (Show,Eq,Data,Typeable)+data SocksVersionNotSupported = SocksVersionNotSupported+ deriving (Show,Data,Typeable) instance Exception SocksError instance Exception SocksVersionNotSupported instance Enum SocksCommand where- toEnum 1 = SocksCommandConnect- toEnum 2 = SocksCommandBind- toEnum 3 = SocksCommandUdpAssociate- toEnum w- | w < 256 = SocksCommandOther $ fromIntegral w- | otherwise = error "socks command is only 8 bits"- fromEnum SocksCommandConnect = 1- fromEnum SocksCommandBind = 2- fromEnum SocksCommandUdpAssociate = 3- fromEnum (SocksCommandOther w) = fromIntegral w+ toEnum 1 = SocksCommandConnect+ toEnum 2 = SocksCommandBind+ toEnum 3 = SocksCommandUdpAssociate+ toEnum w+ | w < 256 = SocksCommandOther $ fromIntegral w+ | otherwise = error "socks command is only 8 bits"+ fromEnum SocksCommandConnect = 1+ fromEnum SocksCommandBind = 2+ fromEnum SocksCommandUdpAssociate = 3+ fromEnum (SocksCommandOther w) = fromIntegral w instance Enum SocksMethod where- toEnum 0 = SocksMethodNone- toEnum 1 = SocksMethodGSSAPI- toEnum 2 = SocksMethodUsernamePassword- toEnum 0xff = SocksMethodNotAcceptable- toEnum w- | w < 256 = SocksMethodOther $ fromIntegral w- | otherwise = error "socks method is only 8 bits"- fromEnum SocksMethodNone = 0- fromEnum SocksMethodGSSAPI = 1- fromEnum SocksMethodUsernamePassword = 2- fromEnum (SocksMethodOther w) = fromIntegral w- fromEnum SocksMethodNotAcceptable = 0xff+ toEnum 0 = SocksMethodNone+ toEnum 1 = SocksMethodGSSAPI+ toEnum 2 = SocksMethodUsernamePassword+ toEnum 0xff = SocksMethodNotAcceptable+ toEnum w+ | w < 256 = SocksMethodOther $ fromIntegral w+ | otherwise = error "socks method is only 8 bits"+ fromEnum SocksMethodNone = 0+ fromEnum SocksMethodGSSAPI = 1+ fromEnum SocksMethodUsernamePassword = 2+ fromEnum (SocksMethodOther w) = fromIntegral w+ fromEnum SocksMethodNotAcceptable = 0xff +instance Enum SocksError where+ fromEnum SocksErrorGeneralServerFailure = 1+ fromEnum SocksErrorConnectionNotAllowedByRule = 2+ fromEnum SocksErrorNetworkUnreachable = 3+ fromEnum SocksErrorHostUnreachable = 4+ fromEnum SocksErrorConnectionRefused = 5+ fromEnum SocksErrorTTLExpired = 6+ fromEnum SocksErrorCommandNotSupported = 7+ fromEnum SocksErrorAddrTypeNotSupported = 8+ fromEnum (SocksErrorOther w) = fromIntegral w+ toEnum 1 = SocksErrorGeneralServerFailure+ toEnum 2 = SocksErrorConnectionNotAllowedByRule+ toEnum 3 = SocksErrorNetworkUnreachable+ toEnum 4 = SocksErrorHostUnreachable+ toEnum 5 = SocksErrorConnectionRefused+ toEnum 6 = SocksErrorTTLExpired+ toEnum 7 = SocksErrorCommandNotSupported+ toEnum 8 = SocksErrorAddrTypeNotSupported+ toEnum w = SocksErrorOther $ fromIntegral w+ instance Enum SocksReply where- fromEnum SocksReplySuccess = 0- fromEnum SocksReplyGeneralServerFailure = 1- fromEnum SocksReplyConnectionNotAllowedByRule = 2- fromEnum SocksReplyNetworkUnreachable = 3- fromEnum SocksReplyHostUnreachable = 4- fromEnum SocksReplyConnectionRefused = 5- fromEnum SocksReplyTTLExpired = 6- fromEnum SocksReplyCommandNotSupported = 7- fromEnum SocksReplyAddrTypeNotSupported = 8- fromEnum (SocksReplyOther w) = fromIntegral w- toEnum 0 = SocksReplySuccess- toEnum 1 = SocksReplyGeneralServerFailure- toEnum 2 = SocksReplyConnectionNotAllowedByRule- toEnum 3 = SocksReplyNetworkUnreachable- toEnum 4 = SocksReplyHostUnreachable- toEnum 5 = SocksReplyConnectionRefused- toEnum 6 = SocksReplyTTLExpired- toEnum 7 = SocksReplyCommandNotSupported- toEnum 8 = SocksReplyAddrTypeNotSupported- toEnum w- | w < 256 = SocksReplyOther $ fromIntegral w- | otherwise = error "sock reply is only 8 bits"+ fromEnum SocksReplySuccess = 0+ fromEnum (SocksReplyError e) = fromEnum e+ toEnum 0 = SocksReplySuccess+ toEnum n = SocksReplyError (toEnum n)
Network/Socks5/Wire.hs view
@@ -6,11 +6,11 @@ -- Stability : experimental -- Portability : unknown module Network.Socks5.Wire- ( SocksHello(..)- , SocksHelloResponse(..)- , SocksRequest(..)- , SocksResponse(..)- ) where+ ( SocksHello(..)+ , SocksHelloResponse(..)+ , SocksRequest(..)+ , SocksResponse(..)+ ) where import Control.Applicative import Control.Monad@@ -21,23 +21,28 @@ import Network.Socks5.Types +-- | Initial message sent by client with the list of authentification methods supported data SocksHello = SocksHello { getSocksHelloMethods :: [SocksMethod] }- deriving (Show,Eq)+ deriving (Show,Eq) +-- | Initial message send by server in return from Hello, with the+-- server chosen method of authentication data SocksHelloResponse = SocksHelloResponse { getSocksHelloResponseMethod :: SocksMethod }- deriving (Show,Eq)+ deriving (Show,Eq) +-- | Define a SOCKS requests data SocksRequest = SocksRequest- { requestCommand :: SocksCommand- , requestDstAddr :: SocksAddr- , requestDstPort :: PortNumber- } deriving (Show,Eq)+ { requestCommand :: SocksCommand+ , requestDstAddr :: SocksHostAddress+ , requestDstPort :: PortNumber+ } deriving (Show,Eq) +-- | Define a SOCKS response data SocksResponse = SocksResponse- { responseReply :: SocksReply- , responseBindAddr :: SocksAddr- , responseBindPort :: PortNumber- } deriving (Show,Eq)+ { responseReply :: SocksReply+ , responseBindAddr :: SocksHostAddress+ , responseBindPort :: PortNumber+ } deriving (Show,Eq) getAddr 1 = SocksAddrIPV4 <$> getWord32be getAddr 3 = SocksAddrDomainName <$> (getWord8 >>= getByteString . fromIntegral)@@ -49,58 +54,57 @@ putAddr (SocksAddrIPV6 (a,b,c,d)) = putWord8 4 >> mapM_ putWord32host [a,b,c,d] getSocksRequest 5 = do- cmd <- toEnum . fromIntegral <$> getWord8- _ <- getWord8- addr <- getWord8 >>= getAddr- port <- fromIntegral <$> getWord16be- return $ SocksRequest cmd addr port+ cmd <- toEnum . fromIntegral <$> getWord8+ _ <- getWord8+ addr <- getWord8 >>= getAddr+ port <- fromIntegral <$> getWord16be+ return $ SocksRequest cmd addr port getSocksRequest v =- error ("unsupported version of the protocol " ++ show v)+ error ("unsupported version of the protocol " ++ show v) getSocksResponse 5 = do- reply <- toEnum . fromIntegral <$> getWord8- _ <- getWord8- addr <- getWord8 >>= getAddr- port <- fromIntegral <$> getWord16be- return $ SocksResponse reply addr port+ reply <- toEnum . fromIntegral <$> getWord8+ _ <- getWord8+ addr <- getWord8 >>= getAddr+ port <- fromIntegral <$> getWord16be+ return $ SocksResponse reply addr port getSocksResponse v =- error ("unsupported version of the protocol " ++ show v)+ error ("unsupported version of the protocol " ++ show v) instance Serialize SocksHello where- put (SocksHello ms) = do- putWord8 5- putWord8 $ fromIntegral $ length ms- mapM_ (putWord8 . fromIntegral . fromEnum) ms- get = do- v <- getWord8- case v of- 5 -> getWord8 >>= flip replicateM (toEnum . fromIntegral <$> getWord8) . fromIntegral >>= return . SocksHello- _ -> error "unsupported sock hello version"+ put (SocksHello ms) = do+ putWord8 5+ putWord8 $ fromIntegral $ length ms+ mapM_ (putWord8 . fromIntegral . fromEnum) ms+ get = do+ v <- getWord8+ case v of+ 5 -> getWord8 >>= flip replicateM (toEnum . fromIntegral <$> getWord8) . fromIntegral >>= return . SocksHello+ _ -> error "unsupported sock hello version" instance Serialize SocksHelloResponse where- put (SocksHelloResponse m) = putWord8 5 >> putWord8 (fromIntegral $ fromEnum $ m)- get = do- v <- getWord8- case v of- 5 -> SocksHelloResponse . toEnum . fromIntegral <$> getWord8- _ -> error "unsupported sock hello response version"+ put (SocksHelloResponse m) = putWord8 5 >> putWord8 (fromIntegral $ fromEnum $ m)+ get = do+ v <- getWord8+ case v of+ 5 -> SocksHelloResponse . toEnum . fromIntegral <$> getWord8+ _ -> error "unsupported sock hello response version" instance Serialize SocksRequest where- put req = do- putWord8 5- putWord8 $ fromIntegral $ fromEnum $ requestCommand req- putWord8 0- putAddr $ requestDstAddr req- putWord16be $ fromIntegral $ requestDstPort req- - get = getWord8 >>= getSocksRequest+ put req = do+ putWord8 5+ putWord8 $ fromIntegral $ fromEnum $ requestCommand req+ putWord8 0+ putAddr $ requestDstAddr req+ putWord16be $ fromIntegral $ requestDstPort req+ + get = getWord8 >>= getSocksRequest instance Serialize SocksResponse where- put req = do- putWord8 5- putWord8 $ fromIntegral $ fromEnum $ responseReply req- putWord8 0- putAddr $ responseBindAddr req- putWord16be $ fromIntegral $ responseBindPort req- get = getWord8 >>= getSocksResponse-+ put req = do+ putWord8 5+ putWord8 $ fromIntegral $ fromEnum $ responseReply req+ putWord8 0+ putAddr $ responseBindAddr req+ putWord16be $ fromIntegral $ responseBindPort req+ get = getWord8 >>= getSocksResponse
socks.cabal view
@@ -1,5 +1,5 @@ Name: socks-Version: 0.4.2+Version: 0.5.0 Description: Socks proxy (version 5) implementation. License: BSD3 License-file: LICENSE@@ -20,8 +20,10 @@ , cereal >= 0.3.1 , network >= 2.3 Exposed-modules: Network.Socks5- Other-modules: Network.Socks5.Wire+ Network.Socks5.Lowlevel Network.Socks5.Types+ Other-modules: Network.Socks5.Wire+ Network.Socks5.Conf Network.Socks5.Command ghc-options: -Wall -fno-warn-missing-signatures