packages feed

socks 0.5.6 → 0.6.0

raw patch · 9 files changed

+95/−153 lines, 9 filesdep +basementdep ~network

Dependencies added: basement

Dependency ranges changed: network

Files

Example.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-} import Network.Socks5-import Network.Socket hiding (recv, sClose)+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,32 +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-            socksConnectWithSocket socket (defaultSocksConfFromSockAddr socksServerAddr)+            (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,31 +33,24 @@     , socksConnectWithSocket     , socksConnect     -- * Variants-    , socksConnectAddr     , socksConnectName-    , socksConnectTo'-    , socksConnectTo-    , socksConnectWith     ) where  import Control.Monad import Control.Exception import qualified Data.ByteString.Char8 as BC-import Network.Socket ( close, 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.@@ -65,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) @@ -77,63 +68,22 @@              -> SocksAddress -- ^ SOCKS Address to connect to.              -> IO (Socket, (SocksHostAddress, PortNumber)) socksConnect serverConf destAddr =-    getProtocolNumber "tcp" >>= \proto ->-    bracketOnError (socket AF_INET Stream proto) close $ \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) close $ \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 Socket-socksConnectTo' sockshost socksport desthost destport = do-    sport <- resolvePortID socksport-    let socksConf = defaultSocksConf sockshost sport-    socksConnectWith socksConf desthost destport---- | 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/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,15 +13,15 @@     , 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)  import Network.Socks5.Types-import Network.Socks5.Parse as P (anyByte, take)  -- | Initial message sent by client with the list of authentification methods supported data SocksHello = SocksHello { getSocksHelloMethods :: [SocksMethod] }@@ -46,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,5 +1,5 @@ Name:                socks-Version:             0.5.6+Version:             0.6.0 Synopsis:            Socks proxy (ver 5) Description:         Socks proxy (version 5) implementation. License:             BSD3@@ -10,7 +10,7 @@ Build-Type:          Simple Category:            Network stability:           experimental-Cabal-Version:       >=1.18+Cabal-Version:       1.18 Homepage:            http://github.com/vincenthz/hs-socks extra-doc-files:     README.md, Example.hs @@ -18,7 +18,8 @@   Build-Depends:     base >= 3 && < 5                    , bytestring                    , cereal >= 0.3.1-                   , network >= 2.4+                   , network >= 2.6+                   , basement   Exposed-modules:   Network.Socks5                      Network.Socks5.Lowlevel                      Network.Socks5.Types