socks 0.5.4 → 0.6.1
raw patch · 10 files changed
Files
- Example.hs +8/−19
- LICENSE +1/−1
- Network/Socks5.hs +19/−61
- Network/Socks5/Command.hs +8/−7
- Network/Socks5/Conf.hs +11/−22
- Network/Socks5/Lowlevel.hs +1/−12
- Network/Socks5/Parse.hs +221/−0
- Network/Socks5/Types.hs +11/−3
- Network/Socks5/Wire.hs +33/−19
- socks.cabal +8/−5
Example.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-} import Network.Socks5-import Network.Socket hiding (recv)+import Network.Socket hiding (recv, close) import Network.Socket.ByteString+import Network.Socket (close) import Network.BSD-import Network import Data.ByteString.Char8 () import qualified Data.ByteString.Char8 as BC @@ -24,34 +25,22 @@ example1 socksServerAddr destinationName example2 socksServerAddr destinationName - example3 serverName serverPort destinationName 80- where -- connect to @destName on port 80 through the socks server -- www.google.com get resolve on the client here and then the sockaddr is -- passed to socksConnectAddr example1 socksServerAddr destName = do- socket <- socket AF_INET Stream defaultProtocol- - gHost <- getHostByName destName- let destinationAddr = SockAddrInet 80 (head $ hostAddresses gHost)- socksConnectAddr socket socksServerAddr destinationAddr+ (socket, _) <- socksConnect (defaultSocksConf socksServerAddr)+ (SocksAddress (SocksAddrDomainName $ BC.pack destName) 80) sendAll socket "GET / HTTP/1.0\r\n\r\n" recv socket 4096 >>= putStrLn . show- sClose socket+ close socket -- connect to @destName on port 80 through the socks server -- the server is doing the resolution itself example2 socksServerAddr destName = do socket <- socket AF_INET Stream defaultProtocol- socksConnectName socket socksServerAddr destName 80+ socksConnectName socket (defaultSocksConf socksServerAddr) destName 80 sendAll socket "GET / HTTP/1.0\r\n\r\n" recv socket 4096 >>= putStrLn . show- sClose socket-- example3 sname sport dname dport = do - handle <- socksConnectTo sname (PortNumber sport) dname (PortNumber dport)- BC.hPut handle "GET / HTTP/1.0\r\n\r\n"- hFlush handle- BC.hGet handle 1024 >>= putStrLn . show- hClose handle+ close socket
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2010-2011 Vincent Hanquez <vincent@snarc.org>+Copyright (c) 2010-2019 Vincent Hanquez <vincent@snarc.org> All rights reserved.
Network/Socks5.hs view
@@ -33,30 +33,24 @@ , socksConnectWithSocket , socksConnect -- * Variants- , socksConnectAddr , socksConnectName- , socksConnectTo- , socksConnectWith ) where import Control.Monad import Control.Exception import qualified Data.ByteString.Char8 as BC-import Network.Socket ( sClose, Socket, SocketType(..), SockAddr(..), Family(..)- , socket, socketToHandle, connect)-import Network.BSD-import Network (PortID(..))+import Network.Socket ( close, Socket, SocketType(..), Family(..)+ , socket, connect, PortNumber, defaultProtocol) import qualified Network.Socks5.Command as Cmd import Network.Socks5.Conf import Network.Socks5.Types import Network.Socks5.Lowlevel -import System.IO---- | connect a user specified new socket to the socks server,--- and connect the stream on the server side to the 'SockAddress' specified.+-- | connect a user specified new socket on the socks server to a destination --+-- The socket in parameter needs to be already connected to the socks server+-- -- |socket|-----sockServer----->|server|----destAddr----->|destination| -- socksConnectWithSocket :: Socket -- ^ Socket to use.@@ -64,9 +58,7 @@ -> 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]+ r <- Cmd.establish (socksVersion serverConf) sock [SocksMethodNone] when (r == SocksMethodNotAcceptable) $ error "cannot connect with no socks method of authentication" Cmd.rpc_ sock (Connect destAddr) @@ -76,56 +68,22 @@ -> 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+ bracketOnError (socket AF_INET Stream defaultProtocol) close $ \sock -> do+ connect sock (socksServer serverConf) 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.+-- | connect a new socket to the socks server, and connect the stream to a FQDN+-- resolved on the server side. ----- a unix sockaddr will raises an exception.+-- The socket needs to *not* be already connected. ----- |socket|-----sockServer----->|server|----destAddr----->|destination|-{-# DEPRECATED socksConnectAddr "use socksConnectWithSocket" #-}-socksConnectAddr :: Socket -> SockAddr -> SockAddr -> IO ()-socksConnectAddr sock sockserver destaddr =- socksConnectWithSocket sock- (defaultSocksConfFromSockAddr sockserver)- (socksServer $ defaultSocksConfFromSockAddr destaddr) >>+-- The destination need to be an ASCII string, otherwise unexpected behavior will ensue.+-- For unicode destination, punycode encoding should be used.+socksConnectName :: Socket -> SocksConf -> String -> PortNumber -> IO ()+socksConnectName sock sockConf destination port = do+ connect sock (socksServer sockConf)+ (_,_) <- socksConnectWithSocket sock sockConf addr 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 = 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.-socksConnectWith :: SocksConf -- ^ SOCKS configuration- -> String -- ^ destination hostname- -> PortID -- ^ destination port- -> IO Socket-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--resolvePortID (Service serv) = getServicePortNumber serv-resolvePortID (PortNumber n) = return n-resolvePortID _ = error "unsupported unix PortID"+ where+ addr = SocksAddress (SocksAddrDomainName $ BC.pack destination) port
Network/Socks5/Command.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE CPP #-} -- | -- Module : Network.Socks5.Command@@ -22,11 +23,11 @@ , waitSerialized ) where -import Control.Applicative-import Control.Exception+import Basement.Compat.Base import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC+import qualified Prelude import Data.Serialize import Network.Socket (Socket, PortNumber, HostAddress, HostAddress6)@@ -35,8 +36,8 @@ import Network.Socks5.Types import Network.Socks5.Wire -establish :: Socket -> [SocksMethod] -> IO SocksMethod-establish socket methods = do+establish :: SocksVersion -> Socket -> [SocksMethod] -> IO SocksMethod+establish SocksVer5 socket methods = do sendAll socket (encode $ SocksHello methods) getSocksHelloResponseMethod <$> runGetDone get (recv socket 4096) @@ -54,7 +55,7 @@ toRequest (Connect (SocksAddress ha port)) = SocksRequest { requestCommand = SocksCommandConnect , requestDstAddr = ha- , requestDstPort = fromIntegral port+ , requestDstPort = Prelude.fromIntegral port } fromRequest req | requestCommand req /= SocksCommandConnect = Nothing@@ -72,7 +73,7 @@ -- TODO: FQDN should only be ascii, maybe putting a "fqdn" data type -- in front to make sure and make the BC.pack safe.-connectDomainName :: Socket -> String -> PortNumber -> IO (SocksHostAddress, PortNumber)+connectDomainName :: Socket -> [Char] -> PortNumber -> IO (SocksHostAddress, PortNumber) connectDomainName socket fqdn port = rpc_ socket $ Connect $ SocksAddress (SocksAddrDomainName $ BC.pack fqdn) port sendSerialized :: Serialize a => Socket -> a -> IO ()@@ -87,7 +88,7 @@ onReply <$> runGetDone get (getMore socket) where onReply res@(responseReply -> reply) = case reply of- SocksReplySuccess -> Right (responseBindAddr res, fromIntegral $ responseBindPort res)+ SocksReplySuccess -> Right (responseBindAddr res, Prelude.fromIntegral $ responseBindPort res) SocksReplyError e -> Left e rpc_ :: Command a => Socket -> a -> IO (SocksHostAddress, PortNumber)
Network/Socks5/Conf.hs view
@@ -9,43 +9,32 @@ 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+import Network.Socks5.Types (SocksVersion(..)) --- | SOCKS configuration structure.+-- | SOCKS identification and 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+ { socksServer :: SockAddr -- ^ Address of server , 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+socksHost :: SocksConf -> SockAddr+socksHost conf = 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+defaultSocksConf :: SockAddr -> SocksConf+defaultSocksConf host = SocksConf host SocksVer5 --- | same as defaultSocksConf except the server address is determined from a 'SockAddr'+-- | same as defaultSocksConf. ----- 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"+-- soft deprecation: use 'defaultSocksConf"+defaultSocksConfFromSockAddr = defaultSocksConf
Network/Socks5/Lowlevel.hs view
@@ -1,25 +1,14 @@ module Network.Socks5.Lowlevel- ( resolveToSockAddr- , socksListen+ ( 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
+ Network/Socks5/Parse.hs view
@@ -0,0 +1,221 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE OverloadedStrings #-}+-- |+-- Module : Network.Socks5.Parse+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : portable+--+-- A very simple bytestring parser related to Parsec and Attoparsec+--+-- Simple example:+--+-- > > parse ((,) <$> take 2 <*> byte 0x20 <*> (bytes "abc" *> anyByte)) "xx abctest"+-- > ParseOK "est" ("xx", 116)+--+module Network.Socks5.Parse+ ( Parser+ , Result(..)+ -- * run the Parser+ , parse+ , parseFeed+ -- * Parser methods+ , byte+ , anyByte+ , bytes+ , take+ , takeWhile+ , takeAll+ , skip+ , skipWhile+ , skipAll+ , takeStorable+ ) where++import Control.Applicative+import Control.Monad+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as B (toForeignPtr)+import Data.Word+import Foreign.Storable (Storable, peekByteOff, sizeOf)+import Foreign.ForeignPtr (withForeignPtr)+import Prelude hiding (take, takeWhile)++import System.IO.Unsafe (unsafePerformIO)++-- | Simple parsing result, that represent respectively:+--+-- * failure: with the error message+--+-- * continuation: that need for more input data+--+-- * success: the remaining unparsed data and the parser value+data Result a =+ ParseFail String+ | ParseMore (ByteString -> Result a)+ | ParseOK ByteString a++instance Show a => Show (Result a) where+ show (ParseFail err) = "ParseFailure: " ++ err+ show (ParseMore _) = "ParseMore _"+ show (ParseOK b a) = "ParseOK " ++ show a ++ " " ++ show b++type Failure r = ByteString -> String -> Result r+type Success a r = ByteString -> a -> Result r++-- | Simple ByteString parser structure+newtype Parser a = Parser+ { runParser :: forall r . ByteString -> Failure r -> Success a r -> Result r }++instance Monad Parser where+ return v = Parser $ \buf _ ok -> ok buf v+ m >>= k = Parser $ \buf err ok ->+ runParser m buf err (\buf' a -> runParser (k a) buf' err ok)+#if MIN_VERSION_base(4,13,0)+instance MonadFail Parser where+#endif+ fail errorMsg = Parser $ \buf err _ -> err buf ("failed: " ++ errorMsg)+instance MonadPlus Parser where+ mzero = fail "Parser.MonadPlus.mzero"+ mplus f g = Parser $ \buf err ok ->+ -- rewrite the err callback of @f to call @g+ runParser f buf (\_ _ -> runParser g buf err ok) ok+instance Functor Parser where+ fmap f p = Parser $ \buf err ok ->+ runParser p buf err (\b a -> ok b (f a))+instance Applicative Parser where+ pure = return+ (<*>) d e = d >>= \b -> e >>= \a -> return (b a)+instance Alternative Parser where+ empty = fail "Parser.Alternative.empty"+ (<|>) = mplus++-- | Run a parser on an @initial ByteString.+--+-- If the Parser need more data than available, the @feeder function+-- is automatically called and fed to the More continuation.+parseFeed :: Monad m => m B.ByteString -> Parser a -> B.ByteString -> m (Result a)+parseFeed feeder p initial = loop $ parse p initial+ where loop (ParseMore k) = feeder >>= (loop . k)+ loop r = return r++-- | Run a Parser on a ByteString and return a 'Result'+parse :: Parser a -> ByteString -> Result a+parse p s = runParser p s (\_ msg -> ParseFail msg) (\b a -> ParseOK b a)++------------------------------------------------------------+getMore :: Parser ()+getMore = Parser $ \buf err ok -> ParseMore $ \nextChunk ->+ if B.null nextChunk+ then err buf "EOL: need more data"+ else ok (B.append buf nextChunk) ()++getAll :: Parser ()+getAll = Parser $ \buf err ok -> ParseMore $ \nextChunk ->+ if B.null nextChunk+ then ok buf ()+ else runParser getAll (B.append buf nextChunk) err ok++flushAll :: Parser ()+flushAll = Parser $ \buf err ok -> ParseMore $ \nextChunk ->+ if B.null nextChunk+ then ok buf ()+ else runParser getAll B.empty err ok++------------------------------------------------------------++-- | Get the next byte from the parser+anyByte :: Parser Word8+anyByte = Parser $ \buf err ok ->+ case B.uncons buf of+ Nothing -> runParser (getMore >> anyByte) buf err ok+ Just (c1,b2) -> ok b2 c1++-- | Parse a specific byte at current position+--+-- if the byte is different than the expected on,+-- this parser will raise a failure.+byte :: Word8 -> Parser ()+byte w = Parser $ \buf err ok ->+ case B.uncons buf of+ Nothing -> runParser (getMore >> byte w) buf err ok+ Just (c1,b2) | c1 == w -> ok b2 ()+ | otherwise -> err buf ("byte " ++ show w ++ " : failed")++-- | Parse a sequence of bytes from current position+--+-- if the following bytes don't match the expected+-- bytestring completely, the parser will raise a failure+bytes :: ByteString -> Parser ()+bytes allExpected = consumeEq allExpected+ where errMsg = "bytes " ++ show allExpected ++ " : failed"++ -- partially consume as much as possible or raise an error.+ consumeEq expected = Parser $ \actual err ok ->+ let eLen = B.length expected in+ if B.length actual >= eLen+ then -- enough data for doing a full match+ let (aMatch,aRem) = B.splitAt eLen actual+ in if aMatch == expected+ then ok aRem ()+ else err actual errMsg+ else -- not enough data, match as much as we have, and then recurse.+ let (eMatch, eRem) = B.splitAt (B.length actual) expected+ in if actual == eMatch+ then runParser (getMore >> consumeEq eRem) B.empty err ok+ else err actual errMsg++------------------------------------------------------------++-- | Take a storable from the current position in the stream+takeStorable :: Storable d+ => Parser d+takeStorable = anyStorable undefined+ where+ anyStorable :: Storable d => d -> Parser d+ anyStorable a = do+ (fptr, off, _) <- B.toForeignPtr <$> take (sizeOf a)+ return $ unsafePerformIO $ withForeignPtr fptr $ \ptr -> peekByteOff ptr off++-- | Take @n bytes from the current position in the stream+take :: Int -> Parser ByteString+take n = Parser $ \buf err ok ->+ if B.length buf >= n+ then let (b1,b2) = B.splitAt n buf in ok b2 b1+ else runParser (getMore >> take n) buf err ok++-- | Take bytes while the @predicate hold from the current position in the stream+takeWhile :: (Word8 -> Bool) -> Parser ByteString+takeWhile predicate = Parser $ \buf err ok ->+ case B.span predicate buf of+ (_, b2) | B.null b2 -> runParser (getMore >> takeWhile predicate) buf err ok+ (b1, b2) -> ok b2 b1++-- | Take the remaining bytes from the current position in the stream+takeAll :: Parser ByteString+takeAll = Parser $ \buf err ok ->+ runParser (getAll >> returnBuffer) buf err ok+ where+ returnBuffer = Parser $ \buf _ ok -> ok B.empty buf++-- | Skip @n bytes from the current position in the stream+skip :: Int -> Parser ()+skip n = Parser $ \buf err ok ->+ if B.length buf >= n+ then ok (B.drop n buf) ()+ else runParser (getMore >> skip (n - B.length buf)) B.empty err ok++-- | Skip bytes while the @predicate hold from the current position in the stream+skipWhile :: (Word8 -> Bool) -> Parser ()+skipWhile p = Parser $ \buf err ok ->+ case B.span p buf of+ (_, b2) | B.null b2 -> runParser (getMore >> skipWhile p) B.empty err ok+ (_, b2) -> ok b2 ()++-- | Skip all the remaining bytes from the current position in the stream+skipAll :: Parser ()+skipAll = Parser $ \buf err ok -> runParser flushAll buf err ok
Network/Socks5/Types.hs view
@@ -16,12 +16,14 @@ , SocksError(..) ) where +import qualified Basement.String as UTF8+import Basement.Compat.IsList import Data.ByteString (ByteString) import Data.Word import Data.Data import Network.Socket (HostAddress, HostAddress6, PortNumber) import Control.Exception-import qualified Data.ByteString.Char8 as BC+import qualified Data.ByteString as B import Numeric (showHex) import Data.List (intersperse) @@ -52,14 +54,20 @@ -- | A Host address on the SOCKS protocol. data SocksHostAddress = SocksAddrIPV4 !HostAddress- | SocksAddrDomainName !ByteString+ | SocksAddrDomainName !FQDN | SocksAddrIPV6 !HostAddress6 deriving (Eq,Ord) +type FQDN = ByteString+ instance Show SocksHostAddress where show (SocksAddrIPV4 ha) = "SocksAddrIPV4(" ++ showHostAddress ha ++ ")" show (SocksAddrIPV6 ha6) = "SocksAddrIPV6(" ++ showHostAddress6 ha6 ++ ")"- show (SocksAddrDomainName dn) = "SocksAddrDomainName(" ++ BC.unpack dn ++ ")"+ show (SocksAddrDomainName dn) = "SocksAddrDomainName(" ++ showFQDN dn ++ ")"++-- | Converts a FQDN to a String+showFQDN :: FQDN -> String+showFQDN bs = toList $ fst $ UTF8.fromBytesLenient $ fromList $ B.unpack bs -- | Converts a HostAddress to a String in dot-decimal notation showHostAddress :: HostAddress -> String
Network/Socks5/Wire.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE NoImplicitPrelude #-} -- | -- Module : Network.Socks5.Wire -- License : BSD-style@@ -12,10 +13,11 @@ , SocksResponse(..) ) where -import Control.Applicative+import Basement.Compat.Base import Control.Monad import qualified Data.ByteString as B import Data.Serialize+import qualified Prelude import Network.Socket (PortNumber) @@ -45,66 +47,78 @@ } deriving (Show,Eq) getAddr 1 = SocksAddrIPV4 <$> getWord32host-getAddr 3 = SocksAddrDomainName <$> (getWord8 >>= getByteString . fromIntegral)+getAddr 3 = SocksAddrDomainName <$> (getLength8 >>= getByteString) getAddr 4 = SocksAddrIPV6 <$> (liftM4 (,,,) getWord32host getWord32host getWord32host getWord32host)-getAddr n = error ("cannot get unknown socket address type: " ++ show n)+getAddr n = error ("cannot get unknown socket address type: " <> show n) putAddr (SocksAddrIPV4 h) = putWord8 1 >> putWord32host h-putAddr (SocksAddrDomainName b) = putWord8 3 >> putWord8 (fromIntegral $ B.length b) >> putByteString b+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 . Prelude.fromIntegral . fromEnum++getEnum8 :: Enum e => Get e+getEnum8 = toEnum . Prelude.fromIntegral <$> getWord8++putLength8 :: Int -> Put+putLength8 = putWord8 . Prelude.fromIntegral++getLength8 :: Get Int+getLength8 = Prelude.fromIntegral <$> getWord8+ getSocksRequest 5 = do- cmd <- toEnum . fromIntegral <$> getWord8+ cmd <- getEnum8 _ <- getWord8 addr <- getWord8 >>= getAddr- port <- fromIntegral <$> getWord16be+ port <- Prelude.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+ reply <- getEnum8 _ <- getWord8 addr <- getWord8 >>= getAddr- port <- fromIntegral <$> getWord16be+ port <- Prelude.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+ putLength8 (Prelude.length ms)+ mapM_ putEnum8 ms get = do v <- getWord8 case v of- 5 -> getWord8 >>= flip replicateM (toEnum . fromIntegral <$> getWord8) . fromIntegral >>= return . SocksHello+ 5 -> SocksHello <$> (getLength8 >>= flip replicateM getEnum8) _ -> error "unsupported sock hello version" instance Serialize SocksHelloResponse where- put (SocksHelloResponse m) = putWord8 5 >> putWord8 (fromIntegral $ fromEnum $ m)+ put (SocksHelloResponse m) = putWord8 5 >> putEnum8 m get = do v <- getWord8 case v of- 5 -> SocksHelloResponse . toEnum . fromIntegral <$> getWord8+ 5 -> SocksHelloResponse <$> getEnum8 _ -> error "unsupported sock hello response version" instance Serialize SocksRequest where put req = do putWord8 5- putWord8 $ fromIntegral $ fromEnum $ requestCommand req+ putEnum8 $ requestCommand req putWord8 0 putAddr $ requestDstAddr req- putWord16be $ fromIntegral $ requestDstPort req+ putWord16be $ Prelude.fromIntegral $ requestDstPort req get = getWord8 >>= getSocksRequest instance Serialize SocksResponse where put req = do putWord8 5- putWord8 $ fromIntegral $ fromEnum $ responseReply req+ putEnum8 $ responseReply req putWord8 0 putAddr $ responseBindAddr req- putWord16be $ fromIntegral $ responseBindPort req+ putWord16be $ Prelude.fromIntegral $ responseBindPort req get = getWord8 >>= getSocksResponse
socks.cabal view
@@ -1,31 +1,34 @@ Name: socks-Version: 0.5.4+Version: 0.6.1+Synopsis: Socks proxy (ver 5) Description: Socks proxy (version 5) implementation. License: BSD3 License-file: LICENSE Copyright: Vincent Hanquez <vincent@snarc.org> Author: Vincent Hanquez <vincent@snarc.org> Maintainer: Vincent Hanquez <vincent@snarc.org>-Synopsis: Socks proxy (version 5) implementation. Build-Type: Simple Category: Network stability: experimental-Cabal-Version: >=1.6+Cabal-Version: 1.18 Homepage: http://github.com/vincenthz/hs-socks-data-files: README.md, Example.hs+extra-doc-files: README.md, Example.hs Library Build-Depends: base >= 3 && < 5 , bytestring , cereal >= 0.3.1- , network >= 2.3+ , network >= 2.6+ , basement Exposed-modules: Network.Socks5 Network.Socks5.Lowlevel Network.Socks5.Types Other-modules: Network.Socks5.Wire Network.Socks5.Conf Network.Socks5.Command+ Network.Socks5.Parse ghc-options: -Wall -fno-warn-missing-signatures -fwarn-tabs+ default-language: Haskell2010 source-repository head type: git