Blame Network/Socks5.hs

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"