{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ViewPatterns #-}
module Network.Socks5
(
SocksAddress(..)
, SocksHostAddress(..)
, SocksReply(..)
, SocksError(..)
, module Network.Socks5.Conf
, socksConnectWithSocket
, socksConnect
, 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 qualified Network.Socks5.Command as Cmd
import Network.Socks5.Conf
import Network.Socks5.Types
import Network.Socks5.Lowlevel
import System.IO
socksConnectWithSocket :: Socket
-> SocksConf
-> SocksAddress
-> 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)
socksConnect :: SocksConf
-> SocksAddress
-> 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)
{-# DEPRECATED socksConnectAddr "use socksConnectWithSocket" #-}
socksConnectAddr :: Socket -> SockAddr -> SockAddr -> IO ()
socksConnectAddr sock sockserver destaddr =
socksConnectWithSocket sock
(defaultSocksConfFromSockAddr sockserver)
(socksServer $ defaultSocksConfFromSockAddr destaddr) >>
return ()
socksConnectName :: Socket -> SockAddr -> String -> PortNumber -> IO ()
socksConnectName sock sockserver destination port = do
socksConnectWithSocket sock
(defaultSocksConfFromSockAddr sockserver)
(SocksAddress (SocksAddrDomainName $ BC.pack destination) port)
>> return ()
socksConnectWith :: SocksConf
-> String
-> PortID
-> 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
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"