|
Packit |
fc2124 |
{-# LANGUAGE DeriveDataTypeable #-}
|
|
Packit |
fc2124 |
{-# LANGUAGE ViewPatterns #-}
|
|
Packit |
fc2124 |
-- |
|
|
Packit |
fc2124 |
-- Module : Network.Socks5
|
|
Packit |
fc2124 |
-- License : BSD-style
|
|
Packit |
fc2124 |
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
|
|
Packit |
fc2124 |
-- Stability : experimental
|
|
Packit |
fc2124 |
-- Portability : unknown
|
|
Packit |
fc2124 |
--
|
|
Packit |
fc2124 |
-- This is an implementation of SOCKS5 as defined in RFC 1928
|
|
Packit |
fc2124 |
--
|
|
Packit |
fc2124 |
-- In Wikipedia's words:
|
|
Packit |
fc2124 |
--
|
|
Packit |
fc2124 |
-- SOCKet Secure (SOCKS) is an Internet protocol that routes network packets
|
|
Packit |
fc2124 |
-- between a client and server through a proxy server. SOCKS5 additionally
|
|
Packit |
fc2124 |
-- provides authentication so only authorized users may access a server.
|
|
Packit |
fc2124 |
-- Practically, a SOCKS server will proxy TCP connections to an arbitrary IP
|
|
Packit |
fc2124 |
-- address as well as providing a means for UDP packets to be forwarded.
|
|
Packit |
fc2124 |
--
|
|
Packit |
fc2124 |
-- BIND and UDP ASSOCIATE messages are not implemented.
|
|
Packit |
fc2124 |
-- However main usage of SOCKS is covered in this implementation.
|
|
Packit |
fc2124 |
--
|
|
Packit |
fc2124 |
module Network.Socks5
|
|
Packit |
fc2124 |
(
|
|
Packit |
fc2124 |
-- * Types
|
|
Packit |
fc2124 |
SocksAddress(..)
|
|
Packit |
fc2124 |
, SocksHostAddress(..)
|
|
Packit |
fc2124 |
, SocksReply(..)
|
|
Packit |
fc2124 |
, SocksError(..)
|
|
Packit |
fc2124 |
-- * Configuration
|
|
Packit |
fc2124 |
, module Network.Socks5.Conf
|
|
Packit |
fc2124 |
-- * Methods
|
|
Packit |
fc2124 |
, socksConnectWithSocket
|
|
Packit |
fc2124 |
, socksConnect
|
|
Packit |
fc2124 |
-- * Variants
|
|
Packit |
fc2124 |
, socksConnectAddr
|
|
Packit |
fc2124 |
, socksConnectName
|
|
Packit |
fc2124 |
, socksConnectTo'
|
|
Packit |
fc2124 |
, socksConnectTo
|
|
Packit |
fc2124 |
, socksConnectWith
|
|
Packit |
fc2124 |
) where
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
import Control.Monad
|
|
Packit |
fc2124 |
import Control.Exception
|
|
Packit |
fc2124 |
import qualified Data.ByteString.Char8 as BC
|
|
Packit |
fc2124 |
import Network.Socket ( close, Socket, SocketType(..), SockAddr(..), Family(..)
|
|
Packit |
fc2124 |
, socket, socketToHandle, connect)
|
|
Packit |
fc2124 |
import Network.BSD
|
|
Packit |
fc2124 |
import Network (PortID(..))
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
import qualified Network.Socks5.Command as Cmd
|
|
Packit |
fc2124 |
import Network.Socks5.Conf
|
|
Packit |
fc2124 |
import Network.Socks5.Types
|
|
Packit |
fc2124 |
import Network.Socks5.Lowlevel
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
import System.IO
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
-- | connect a user specified new socket to the socks server,
|
|
Packit |
fc2124 |
-- and connect the stream on the server side to the 'SockAddress' specified.
|
|
Packit |
fc2124 |
--
|
|
Packit |
fc2124 |
-- |socket|-----sockServer----->|server|----destAddr----->|destination|
|
|
Packit |
fc2124 |
--
|
|
Packit |
fc2124 |
socksConnectWithSocket :: Socket -- ^ Socket to use.
|
|
Packit |
fc2124 |
-> SocksConf -- ^ SOCKS configuration for the server.
|
|
Packit |
fc2124 |
-> SocksAddress -- ^ SOCKS Address to connect to.
|
|
Packit |
fc2124 |
-> IO (SocksHostAddress, PortNumber)
|
|
Packit |
fc2124 |
socksConnectWithSocket sock serverConf destAddr = do
|
|
Packit |
fc2124 |
serverAddr <- resolveToSockAddr (socksServer serverConf)
|
|
Packit |
fc2124 |
connect sock serverAddr
|
|
Packit |
fc2124 |
r <- Cmd.establish sock [SocksMethodNone]
|
|
Packit |
fc2124 |
when (r == SocksMethodNotAcceptable) $ error "cannot connect with no socks method of authentication"
|
|
Packit |
fc2124 |
Cmd.rpc_ sock (Connect destAddr)
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
-- | connect a new socket to a socks server and connect the stream on the
|
|
Packit |
fc2124 |
-- server side to the 'SocksAddress' specified.
|
|
Packit |
fc2124 |
socksConnect :: SocksConf -- ^ SOCKS configuration for the server.
|
|
Packit |
fc2124 |
-> SocksAddress -- ^ SOCKS Address to connect to.
|
|
Packit |
fc2124 |
-> IO (Socket, (SocksHostAddress, PortNumber))
|
|
Packit |
fc2124 |
socksConnect serverConf destAddr =
|
|
Packit |
fc2124 |
getProtocolNumber "tcp" >>= \proto ->
|
|
Packit |
fc2124 |
bracketOnError (socket AF_INET Stream proto) close $ \sock -> do
|
|
Packit |
fc2124 |
ret <- socksConnectWithSocket sock serverConf destAddr
|
|
Packit |
fc2124 |
return (sock, ret)
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
-- | connect a new socket to the socks server, and connect the stream on the server side
|
|
Packit |
fc2124 |
-- to the sockaddr specified. the sockaddr need to be SockAddrInet or SockAddrInet6.
|
|
Packit |
fc2124 |
--
|
|
Packit |
fc2124 |
-- a unix sockaddr will raises an exception.
|
|
Packit |
fc2124 |
--
|
|
Packit |
fc2124 |
-- |socket|-----sockServer----->|server|----destAddr----->|destination|
|
|
Packit |
fc2124 |
{-# DEPRECATED socksConnectAddr "use socksConnectWithSocket" #-}
|
|
Packit |
fc2124 |
socksConnectAddr :: Socket -> SockAddr -> SockAddr -> IO ()
|
|
Packit |
fc2124 |
socksConnectAddr sock sockserver destaddr =
|
|
Packit |
fc2124 |
socksConnectWithSocket sock
|
|
Packit |
fc2124 |
(defaultSocksConfFromSockAddr sockserver)
|
|
Packit |
fc2124 |
(socksServer $ defaultSocksConfFromSockAddr destaddr) >>
|
|
Packit |
fc2124 |
return ()
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
-- | connect a new socket to the socks server, and connect the stream to a FQDN
|
|
Packit |
fc2124 |
-- resolved on the server side.
|
|
Packit |
fc2124 |
socksConnectName :: Socket -> SockAddr -> String -> PortNumber -> IO ()
|
|
Packit |
fc2124 |
socksConnectName sock sockserver destination port = do
|
|
Packit |
fc2124 |
socksConnectWithSocket sock
|
|
Packit |
fc2124 |
(defaultSocksConfFromSockAddr sockserver)
|
|
Packit |
fc2124 |
(SocksAddress (SocksAddrDomainName $ BC.pack destination) port)
|
|
Packit |
fc2124 |
>> return ()
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
-- | create a new socket and connect in to a destination through the specified
|
|
Packit |
fc2124 |
-- SOCKS configuration.
|
|
Packit |
fc2124 |
socksConnectWith :: SocksConf -- ^ SOCKS configuration
|
|
Packit |
fc2124 |
-> String -- ^ destination hostname
|
|
Packit |
fc2124 |
-> PortID -- ^ destination port
|
|
Packit |
fc2124 |
-> IO Socket
|
|
Packit |
fc2124 |
socksConnectWith socksConf desthost destport = do
|
|
Packit |
fc2124 |
dport <- resolvePortID destport
|
|
Packit |
fc2124 |
proto <- getProtocolNumber "tcp"
|
|
Packit |
fc2124 |
bracketOnError (socket AF_INET Stream proto) close $ \sock -> do
|
|
Packit |
fc2124 |
sockaddr <- resolveToSockAddr (socksServer socksConf)
|
|
Packit |
fc2124 |
socksConnectName sock sockaddr desthost dport
|
|
Packit |
fc2124 |
return sock
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
-- | similar to Network connectTo but use a socks proxy with default socks configuration.
|
|
Packit |
fc2124 |
socksConnectTo' :: String -> PortID -> String -> PortID -> IO Socket
|
|
Packit |
fc2124 |
socksConnectTo' sockshost socksport desthost destport = do
|
|
Packit |
fc2124 |
sport <- resolvePortID socksport
|
|
Packit |
fc2124 |
let socksConf = defaultSocksConf sockshost sport
|
|
Packit |
fc2124 |
socksConnectWith socksConf desthost destport
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
-- | similar to Network connectTo but use a socks proxy with default socks configuration.
|
|
Packit |
fc2124 |
socksConnectTo :: String -> PortID -> String -> PortID -> IO Handle
|
|
Packit |
fc2124 |
socksConnectTo sockshost socksport desthost destport = do
|
|
Packit |
fc2124 |
sport <- resolvePortID socksport
|
|
Packit |
fc2124 |
let socksConf = defaultSocksConf sockshost sport
|
|
Packit |
fc2124 |
sock <- socksConnectWith socksConf desthost destport
|
|
Packit |
fc2124 |
socketToHandle sock ReadWriteMode
|
|
Packit |
fc2124 |
|
|
Packit |
fc2124 |
resolvePortID (Service serv) = getServicePortNumber serv
|
|
Packit |
fc2124 |
resolvePortID (PortNumber n) = return n
|
|
Packit |
fc2124 |
resolvePortID _ = error "unsupported unix PortID"
|