{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ViewPatterns #-}
module Network.Socks5
(
SocksAddress(..)
, SocksHostAddress(..)
, SocksReply(..)
, SocksError(..)
, module Network.Socks5.Conf
, socksConnectWithSocket
, socksConnect
, socksConnectName
) where
import Control.Monad
import Control.Exception
import qualified Data.ByteString.Char8 as BC
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
socksConnectWithSocket :: Socket
-> SocksConf
-> SocksAddress
-> IO (SocksHostAddress, PortNumber)
socksConnectWithSocket :: Socket
-> SocksConf -> SocksAddress -> IO (SocksHostAddress, PortNumber)
socksConnectWithSocket Socket
sock SocksConf
serverConf SocksAddress
destAddr = do
SocksMethod
r <- SocksVersion -> Socket -> [SocksMethod] -> IO SocksMethod
Cmd.establish (SocksConf -> SocksVersion
socksVersion SocksConf
serverConf) Socket
sock [SocksMethod
SocksMethodNone]
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (SocksMethod
r SocksMethod -> SocksMethod -> Bool
forall a. Eq a => a -> a -> Bool
== SocksMethod
SocksMethodNotAcceptable) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char] -> IO ()
forall a. HasCallStack => [Char] -> a
error [Char]
"cannot connect with no socks method of authentication"
Socket -> Connect -> IO (SocksHostAddress, PortNumber)
forall a.
Command a =>
Socket -> a -> IO (SocksHostAddress, PortNumber)
Cmd.rpc_ Socket
sock (SocksAddress -> Connect
Connect SocksAddress
destAddr)
socksConnect :: SocksConf
-> SocksAddress
-> IO (Socket, (SocksHostAddress, PortNumber))
socksConnect :: SocksConf
-> SocksAddress -> IO (Socket, (SocksHostAddress, PortNumber))
socksConnect SocksConf
serverConf SocksAddress
destAddr =
IO Socket
-> (Socket -> IO ())
-> (Socket -> IO (Socket, (SocksHostAddress, PortNumber)))
-> IO (Socket, (SocksHostAddress, PortNumber))
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracketOnError (Family -> SocketType -> ProtocolNumber -> IO Socket
socket Family
AF_INET SocketType
Stream ProtocolNumber
defaultProtocol) Socket -> IO ()
close ((Socket -> IO (Socket, (SocksHostAddress, PortNumber)))
-> IO (Socket, (SocksHostAddress, PortNumber)))
-> (Socket -> IO (Socket, (SocksHostAddress, PortNumber)))
-> IO (Socket, (SocksHostAddress, PortNumber))
forall a b. (a -> b) -> a -> b
$ \Socket
sock -> do
Socket -> SockAddr -> IO ()
connect Socket
sock (SocksConf -> SockAddr
socksServer SocksConf
serverConf)
(SocksHostAddress, PortNumber)
ret <- Socket
-> SocksConf -> SocksAddress -> IO (SocksHostAddress, PortNumber)
socksConnectWithSocket Socket
sock SocksConf
serverConf SocksAddress
destAddr
(Socket, (SocksHostAddress, PortNumber))
-> IO (Socket, (SocksHostAddress, PortNumber))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Socket
sock, (SocksHostAddress, PortNumber)
ret)
socksConnectName :: Socket -> SocksConf -> String -> PortNumber -> IO ()
socksConnectName :: Socket -> SocksConf -> [Char] -> PortNumber -> IO ()
socksConnectName Socket
sock SocksConf
sockConf [Char]
destination PortNumber
port = do
Socket -> SockAddr -> IO ()
connect Socket
sock (SocksConf -> SockAddr
socksServer SocksConf
sockConf)
(SocksHostAddress
_,PortNumber
_) <- Socket
-> SocksConf -> SocksAddress -> IO (SocksHostAddress, PortNumber)
socksConnectWithSocket Socket
sock SocksConf
sockConf SocksAddress
addr
() -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
addr :: SocksAddress
addr = SocksHostAddress -> PortNumber -> SocksAddress
SocksAddress (FQDN -> SocksHostAddress
SocksAddrDomainName (FQDN -> SocksHostAddress) -> FQDN -> SocksHostAddress
forall a b. (a -> b) -> a -> b
$ [Char] -> FQDN
BC.pack [Char]
destination) PortNumber
port