crypton-socks-0.6.2: src/Network/Socks5.hs
{- |
Module : Network.Socks5
License : BSD-style
Copyright : (c) 2010-2019 Vincent Hanquez <vincent@snarc.org>
Stability : experimental
Portability : unknown
This is an implementation of the SOCKS Protocol Version 5 as defined in
[RFC 1928](https://datatracker.ietf.org/doc/html/rfc1928).
In Wikipedia's words:
SOCKS is an Internet protocol that exchanges network packets between a client
and server through a proxy server. SOCKS5 optionally provides authentication
so only authorized users may access a server. Practically, a SOCKS server
proxies TCP connections to an arbitrary IP address, and provides a means for
UDP packets to be forwarded. A SOCKS server accepts incoming client connection
on TCP port 1080.
BIND and UDP ASSOCIATE messages are not implemented. However the main usage of
SOCKS is implemented.
-}
module Network.Socks5
( -- * Types
SocksAddress (..)
, SocksHostAddress (..)
, SocksReply (..)
, SocksError (..)
-- * Configuration
, SocksConf (..)
, socksHost
, defaultSocksConf
, defaultSocksConfFromSockAddr
-- * Methods
, socksConnectWithSocket
, socksConnect
-- * Variants
, socksConnectName
) where
import Control.Exception ( bracketOnError )
import Control.Monad ( when )
import Data.ByteString.Char8 ( pack )
import Network.Socket
( close, Socket, SocketType(..), Family(..), socket, connect
, PortNumber, defaultProtocol
)
import Network.Socks5.Command ( Connect (..), establish, rpc_ )
import Network.Socks5.Conf
( SocksConf (..), defaultSocksConf
, defaultSocksConfFromSockAddr, socksHost
)
import Network.Socks5.Types
( SocksAddress (..), SocksError (..), SocksHostAddress (..)
, SocksMethod (..), SocksReply (..)
)
-- | Connect a user-specified new socket on the SOCKS server to a destination.
--
-- The specified socket needs to be connected to the SOCKS server already.
--
-- |socket|-----sockServer----->|server|----destAddr----->|destination|
--
socksConnectWithSocket ::
Socket -- ^ The socket to use.
-> SocksConf -- ^ The SOCKS configuration for the server.
-> SocksAddress -- ^ The SOCKS address to connect to.
-> IO (SocksHostAddress, PortNumber)
socksConnectWithSocket sock serverConf destAddr = do
r <- establish (socksVersion serverConf) sock [SocksMethodNone]
when (r == SocksMethodNotAcceptable) $
error "cannot connect with no socks method of authentication"
rpc_ sock (Connect destAddr)
-- | Connect a new socket to a SOCKS server and connect the stream on the
-- server side to the specified SOCKS address.
socksConnect ::
SocksConf
-- ^ The SOCKS configuration for the server.
-> SocksAddress
-- ^ The SOCKS address to connect to.
-> IO (Socket, (SocksHostAddress, PortNumber))
socksConnect serverConf destAddr =
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 to a
-- fully-qualified domain name (FQDN) resolved on the server side.
socksConnectName ::
Socket
-- ^ The socket to use. The socket must *not* be connected already.
-> SocksConf
-- ^ The SOCKS configuration for the server.
-> String
-- ^ Destination FQDN. Should comprise only ASCII characters, otherwise
-- unexpected behaviour will ensure. For FQDN including other Unicode code
-- points, Punycode encoding should be used.
-> PortNumber
-- ^ The port number to use.
-> IO ()
socksConnectName sock sockConf destination port = do
connect sock (socksServer sockConf)
(_, _) <- socksConnectWithSocket sock sockConf addr
return ()
where
addr = SocksAddress (SocksAddrDomainName $ pack destination) port