diff --git a/Network/Socks5.hs b/Network/Socks5.hs
--- a/Network/Socks5.hs
+++ b/Network/Socks5.hs
@@ -7,42 +7,76 @@
 -- Stability   : experimental
 -- Portability : unknown
 --
+-- This is an implementation of SOCKS5 as defined in RFC 1928
+--
+-- In Wikipedia's words:
+--
+--   SOCKet Secure (SOCKS) is an Internet protocol that routes network packets
+--   between a client and server through a proxy server. SOCKS5 additionally
+--   provides authentication so only authorized users may access a server.
+--   Practically, a SOCKS server will proxy TCP connections to an arbitrary IP
+--   address as well as providing a means for UDP packets to be forwarded.
+--
+-- BIND and UDP ASSOCIATE messages are not implemented.
+-- However main usage of SOCKS is covered in this implementation.
+--
 module Network.Socks5
-	( SocksConf(..)
-	, defaultSocksConf
-	, socksConnectAddr
-	, socksConnectName
-	, socksConnectTo
-	, socksConnectWith
-	) where
+    (
+    -- * Types
+      SocksAddress(..)
+    , SocksHostAddress(..)
+    , SocksReply(..)
+    , SocksError(..)
+    -- * Configuration
+    , module Network.Socks5.Conf
+    -- * Methods
+    , socksConnectWithSocket
+    , socksConnect
+    -- * Variants
+    , socksConnectAddr
+    , socksConnectName
+    , socksConnectTo
+    , socksConnectWith
+    ) where
 
 import Control.Monad
 import Control.Exception
+import qualified Data.ByteString.Char8 as BC
 import Network.Socket
 import Network.BSD
-import Network.Socks5.Command
+import qualified Network.Socks5.Command as Cmd
+import Network.Socks5.Conf
 import Network.Socks5.Types
+import Network.Socks5.Lowlevel
 import Network
 import System.IO
 
--- | SOCKS configuration structure.
--- this structure will be extended in future to support authentification.
--- use defaultSocksConf to create new record.
-data SocksConf = SocksConf
-	{ socksHost    :: String     -- ^ SOCKS host.
-	, socksPort    :: PortNumber -- ^ SOCKS port.
-	, socksVersion :: Int        -- ^ SOCKS version to use, only 5 supported for now.
-	}
-
--- | defaultSocksConf create a new record, making sure
--- API remains compatible when the record is extended.
-defaultSocksConf host port = SocksConf host port 5
+-- | connect a user specified new socket to the socks server,
+-- and connect the stream on the server side to the 'SockAddress' specified.
+--
+-- |socket|-----sockServer----->|server|----destAddr----->|destination|
+--
+socksConnectWithSocket :: Socket       -- ^ Socket to use.
+                       -> SocksConf    -- ^ SOCKS configuration for the server.
+                       -> SocksAddress -- ^ SOCKS Address to connect to.
+                       -> 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)
 
-withSocks sock sockaddr f = do
-	connect sock sockaddr
-	r <- socks5Establish sock [SocksMethodNone]
-	when (r == SocksMethodNotAcceptable) $ error "cannot connect with no socks method of authentication"
-	f
+-- | connect a new socket to a socks server and connect the stream on the
+-- server side to the 'SocksAddress' specified.
+socksConnect :: SocksConf    -- ^ SOCKS configuration for the server.
+             -> SocksAddress -- ^ SOCKS Address to connect to.
+             -> 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)
 
 -- | connect a new socket to the socks server, and connect the stream on the server side
 -- to the sockaddr specified. the sockaddr need to be SockAddrInet or SockAddrInet6.
@@ -50,19 +84,22 @@
 -- a unix sockaddr will raises an exception.
 --
 -- |socket|-----sockServer----->|server|----destAddr----->|destination|
+{-# DEPRECATED socksConnectAddr "use socksConnectWithSocket" #-}
 socksConnectAddr :: Socket -> SockAddr -> SockAddr -> IO ()
-socksConnectAddr sock sockserver destaddr = withSocks sock sockserver $ do
-	case destaddr of
-		SockAddrInet p h      -> socks5ConnectIPV4 sock h p >> return ()
-		SockAddrInet6 p _ h _ -> socks5ConnectIPV6 sock h p >> return ()
-		_                     -> error "unsupported unix sockaddr type"
+socksConnectAddr sock sockserver destaddr =
+    socksConnectWithSocket sock
+                           (defaultSocksConfFromSockAddr sockserver)
+                           (socksServer $ defaultSocksConfFromSockAddr destaddr) >>
+    return ()
 
 -- | connect a new socket to the socks server, and connect the stream to a FQDN
 -- resolved on the server side.
 socksConnectName :: Socket -> SockAddr -> String -> PortNumber -> IO ()
-socksConnectName sock sockserver destination port = withSocks sock sockserver $ do
-	_ <- socks5ConnectDomainName sock destination port
-	return ()
+socksConnectName sock sockserver destination port = do
+    socksConnectWithSocket sock
+                           (defaultSocksConfFromSockAddr sockserver)
+                           (SocksAddress (SocksAddrDomainName $ BC.pack destination) port)
+    >> return ()
 
 -- | create a new socket and connect in to a destination through the specified
 -- SOCKS configuration.
@@ -70,22 +107,21 @@
                  -> String    -- ^ destination hostname
                  -> PortID    -- ^ destination port
                  -> IO Socket
-socksConnectWith sockConf desthost destport = do
-	dport <- resolvePortID destport
-	proto <- getProtocolNumber "tcp"
-	bracketOnError (socket AF_INET Stream proto) sClose $ \sock -> do
-		he <- getHostByName $ socksHost sockConf
-		let sockaddr = SockAddrInet (socksPort sockConf) (hostAddress he)
-		socksConnectName sock sockaddr desthost dport
-		return sock
+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
 
 -- | similar to Network connectTo but use a socks proxy with default socks configuration.
 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
+    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
diff --git a/Network/Socks5/Command.hs b/Network/Socks5/Command.hs
--- a/Network/Socks5/Command.hs
+++ b/Network/Socks5/Command.hs
@@ -8,13 +8,18 @@
 -- Portability : unknown
 --
 module Network.Socks5.Command
-	( socks5Establish
-	, socks5ConnectIPV4
-	, socks5ConnectIPV6
-	, socks5ConnectDomainName
-	-- * lowlevel interface
-	, socks5Rpc
-	) where
+    ( establish
+    , Connect(..)
+    , Command(..)
+    , connectIPV4
+    , connectIPV6
+    , connectDomainName
+    -- * lowlevel interface
+    , rpc
+    , rpc_
+    , sendSerialized
+    , waitSerialized
+    ) where
 
 import Control.Applicative
 import Control.Exception
@@ -29,56 +34,73 @@
 import Network.Socks5.Types
 import Network.Socks5.Wire
 
-socks5Establish :: Socket -> [SocksMethod] -> IO SocksMethod
-socks5Establish socket methods = do
-	sendAll socket (encode $ SocksHello methods)
-	getSocksHelloResponseMethod <$> runGetDone get (recv socket 4096)
+establish :: Socket -> [SocksMethod] -> IO SocksMethod
+establish socket methods = do
+    sendAll socket (encode $ SocksHello methods)
+    getSocksHelloResponseMethod <$> runGetDone get (recv socket 4096)
 
-socks5ConnectIPV4 :: Socket -> HostAddress -> PortNumber -> IO (HostAddress, PortNumber)
-socks5ConnectIPV4 socket hostaddr port = onReply <$> socks5Rpc socket request
-	where
-		request = SocksRequest
-			{ requestCommand  = SocksCommandConnect
-			, requestDstAddr  = SocksAddrIPV4 hostaddr
-			, requestDstPort  = fromIntegral port
-			}
+newtype Connect = Connect SocksAddress deriving (Show,Eq)
 
-		onReply (SocksAddrIPV4 h, p) = (h, p)
-		onReply _                    = error "ipv4 requested, got something different"
+class Command a where
+    toRequest   :: a -> SocksRequest
+    fromRequest :: SocksRequest -> Maybe a
 
-socks5ConnectIPV6 :: Socket -> HostAddress6 -> PortNumber -> IO (HostAddress6, PortNumber)
-socks5ConnectIPV6 socket hostaddr6 port = onReply <$> socks5Rpc socket request
-	where
-		request = SocksRequest
-			{ requestCommand  = SocksCommandConnect
-			, requestDstAddr  = SocksAddrIPV6 hostaddr6
-			, requestDstPort  = fromIntegral port
-			}
-		onReply (SocksAddrIPV6 h, p) = (h, p)
-		onReply _                    = error "ipv6 requested, got something different"
+instance Command SocksRequest where
+    toRequest   = id
+    fromRequest = Just
 
+instance Command Connect where
+    toRequest (Connect (SocksAddress ha port)) = SocksRequest
+            { requestCommand  = SocksCommandConnect
+            , requestDstAddr  = ha
+            , requestDstPort  = fromIntegral port
+            }
+    fromRequest req
+        | requestCommand req /= SocksCommandConnect = Nothing
+        | otherwise = Just $ Connect $ SocksAddress (requestDstAddr req) (requestDstPort req)
+
+connectIPV4 :: Socket -> HostAddress -> PortNumber -> IO (HostAddress, PortNumber)
+connectIPV4 socket hostaddr port = onReply <$> rpc_ socket (Connect $ SocksAddress (SocksAddrIPV4 hostaddr) port)
+    where onReply (SocksAddrIPV4 h, p) = (h, p)
+          onReply _                    = error "ipv4 requested, got something different"
+
+connectIPV6 :: Socket -> HostAddress6 -> PortNumber -> IO (HostAddress6, PortNumber)
+connectIPV6 socket hostaddr6 port = onReply <$> rpc_ socket (Connect $ SocksAddress (SocksAddrIPV6 hostaddr6) port)
+    where onReply (SocksAddrIPV6 h, p) = (h, p)
+          onReply _                    = error "ipv6 requested, got something different"
+
 -- TODO: FQDN should only be ascii, maybe putting a "fqdn" data type
 -- in front to make sure and make the BC.pack safe.
-socks5ConnectDomainName :: Socket -> String -> PortNumber -> IO (SocksAddr, PortNumber)
-socks5ConnectDomainName socket fqdn port = socks5Rpc socket $ SocksRequest
-	{ requestCommand  = SocksCommandConnect
-	, requestDstAddr  = SocksAddrDomainName $ BC.pack fqdn
-	, requestDstPort  = fromIntegral port
-	}
+connectDomainName :: Socket -> String -> PortNumber -> IO (SocksHostAddress, PortNumber)
+connectDomainName socket fqdn port = rpc_ socket $ Connect $ SocksAddress (SocksAddrDomainName $ BC.pack fqdn) port
 
-socks5Rpc :: Socket -> SocksRequest -> IO (SocksAddr, PortNumber)
-socks5Rpc socket req = do
-	sendAll socket (encode req)
-	onReply <$> runGetDone get (recv socket 4096)
-	where onReply res@(responseReply -> reply)
-		| reply /= SocksReplySuccess = throw $ SocksError reply
-		| otherwise                 =
-			(responseBindAddr res, fromIntegral $ responseBindPort res)
+sendSerialized :: Serialize a => Socket -> a -> IO ()
+sendSerialized sock a = sendAll sock $ encode a
 
-runGetDone :: Show a => Get a -> IO ByteString -> IO a
+waitSerialized :: Serialize a => Socket -> IO a
+waitSerialized sock = runGetDone get (getMore sock)
+
+rpc :: Command a => Socket -> a -> IO (Either SocksError (SocksHostAddress, PortNumber))
+rpc socket req = do
+    sendSerialized socket (toRequest req)
+    onReply <$> runGetDone get (getMore socket)
+    where onReply res@(responseReply -> reply) =
+                case reply of
+                    SocksReplySuccess -> Right (responseBindAddr res, fromIntegral $ responseBindPort res)
+                    SocksReplyError e -> Left e
+
+rpc_ :: Command a => Socket -> a -> IO (SocksHostAddress, PortNumber)
+rpc_ socket req = rpc socket req >>= either throwIO return
+
+-- this function expect all the data to be consumed. this is fine for intertwined message,
+-- but might not be a good idea for multi messages from one party.
+runGetDone :: Serialize a => Get a -> IO ByteString -> IO a
 runGetDone getter ioget = ioget >>= return . runGetPartial getter >>= r where
-	r (Fail s)       = error s
-	r (Partial cont) = ioget >>= r . cont
-	r (Done a b)
-		| not $ B.null b = error "got too many bytes while receiving data"
-		| otherwise      = return a
+    r (Fail s)       = error s
+    r (Partial cont) = ioget >>= r . cont
+    r (Done a b)
+        | not $ B.null b = error "got too many bytes while receiving data"
+        | otherwise      = return a
+
+getMore :: Socket -> IO ByteString
+getMore socket = recv socket 4096
diff --git a/Network/Socks5/Conf.hs b/Network/Socks5/Conf.hs
new file mode 100644
--- /dev/null
+++ b/Network/Socks5/Conf.hs
@@ -0,0 +1,43 @@
+module Network.Socks5.Conf
+    ( SocksConf(..)
+    , socksHost
+    , socksPort
+    , defaultSocksConf 
+    , defaultSocksConfFromSockAddr
+    ) where
+
+import Network.Socket
+import Network.Socks5.Types (SocksAddress(..), SocksHostAddress(..), SocksVersion(..))
+import qualified Data.ByteString.Char8 as BC
+
+-- | SOCKS configuration structure.
+-- this structure will be extended in future to support authentification.
+-- use defaultSocksConf to create new record.
+data SocksConf = SocksConf
+    { socksServer  :: SocksAddress -- ^ SOCKS Address
+    , socksVersion :: SocksVersion -- ^ SOCKS version to use
+    }
+
+-- | SOCKS Host
+socksHost :: SocksConf -> SocksHostAddress
+socksHost conf = ha where (SocksAddress ha _) = socksServer conf
+
+-- | SOCKS Port
+socksPort :: SocksConf -> PortNumber
+socksPort conf = port where (SocksAddress _ port) = socksServer conf
+
+-- | defaultSocksConf create a new record, making sure
+-- API remains compatible when the record is extended.
+defaultSocksConf host port = SocksConf server SocksVer5
+    where server = SocksAddress haddr port
+          haddr  = SocksAddrDomainName $ BC.pack host
+
+-- | same as defaultSocksConf except the server address is determined from a 'SockAddr'
+--
+-- A unix SockAddr will raises an error. Only Inet and Inet6 types supported
+defaultSocksConfFromSockAddr sockaddr = SocksConf server SocksVer5
+    where server       = SocksAddress haddr port
+          (haddr,port) = case sockaddr of
+                             SockAddrInet p h      -> (SocksAddrIPV4 h, p)
+                             SockAddrInet6 p _ h _ -> (SocksAddrIPV6 h, p)
+                             _                     -> error "unsupported unix sockaddr type"
diff --git a/Network/Socks5/Lowlevel.hs b/Network/Socks5/Lowlevel.hs
new file mode 100644
--- /dev/null
+++ b/Network/Socks5/Lowlevel.hs
@@ -0,0 +1,29 @@
+module Network.Socks5.Lowlevel
+    ( resolveToSockAddr
+    , socksListen
+    -- * lowlevel types
+    , module Network.Socks5.Wire
+    , module Network.Socks5.Command
+    ) where
+
+import Network.Socket
+import Network.BSD
+import Network.Socks5.Command
+import Network.Socks5.Wire
+import Network.Socks5.Types
+import qualified Data.ByteString.Char8 as BC
+
+resolveToSockAddr :: SocksAddress -> IO SockAddr
+resolveToSockAddr (SocksAddress sockHostAddr port) =
+    case sockHostAddr of
+        SocksAddrIPV4 ha       -> return $ SockAddrInet port ha
+        SocksAddrIPV6 ha6      -> return $ SockAddrInet6 port 0 ha6 0
+        SocksAddrDomainName bs -> do he <- getHostByName (BC.unpack bs)
+                                     return $ SockAddrInet port (hostAddress he)
+
+socksListen :: Socket -> IO SocksRequest
+socksListen sock = do
+    hello <- waitSerialized sock
+    case getSocksHelloMethods hello of
+        _ -> do sendSerialized sock (SocksHelloResponse SocksMethodNone)
+                waitSerialized sock
diff --git a/Network/Socks5/Types.hs b/Network/Socks5/Types.hs
--- a/Network/Socks5/Types.hs
+++ b/Network/Socks5/Types.hs
@@ -6,109 +6,121 @@
 -- Stability   : experimental
 -- Portability : unknown
 module Network.Socks5.Types
-	( SocksCommand(..)
-	, SocksMethod(..)
-	, SocksAddr(..)
-	, SocksReply(..)
-	, SocksVersionNotSupported(..)
-	, SocksError(..)
-	) where
+    ( SocksVersion(..)
+    , SocksCommand(..)
+    , SocksMethod(..)
+    , SocksHostAddress(..)
+    , SocksAddress(..)
+    , SocksReply(..)
+    , SocksVersionNotSupported(..)
+    , SocksError(..)
+    ) where
 
 import Data.ByteString (ByteString)
 import Data.Word
 import Data.Data
-import Network.Socket (HostAddress, HostAddress6)
+import Network.Socket (HostAddress, HostAddress6, PortNumber)
 import Control.Exception
 
+-- | Socks Version
+data SocksVersion = SocksVer5
+                  deriving (Show,Eq)
+
 data SocksCommand =
-	  SocksCommandConnect
-	| SocksCommandBind
-	| SocksCommandUdpAssociate
-	| SocksCommandOther Word8
-	deriving (Show,Eq,Ord)
+      SocksCommandConnect
+    | SocksCommandBind
+    | SocksCommandUdpAssociate
+    | SocksCommandOther Word8
+    deriving (Show,Eq,Ord)
 
 data SocksMethod =
-	  SocksMethodNone
-	| SocksMethodGSSAPI
-	| SocksMethodUsernamePassword
-	| SocksMethodOther Word8
-	| SocksMethodNotAcceptable
-	deriving (Show,Eq,Ord)
+      SocksMethodNone
+    | SocksMethodGSSAPI
+    | SocksMethodUsernamePassword
+    | SocksMethodOther Word8
+    | SocksMethodNotAcceptable
+    deriving (Show,Eq,Ord)
 
-data SocksAddr =
-	  SocksAddrIPV4 HostAddress
-	| SocksAddrDomainName ByteString
-	| SocksAddrIPV6 HostAddress6
-	deriving (Show,Eq)
+data SocksHostAddress =
+      SocksAddrIPV4 HostAddress
+    | SocksAddrDomainName ByteString
+    | SocksAddrIPV6 HostAddress6
+    deriving (Show,Eq)
 
+data SocksAddress = SocksAddress SocksHostAddress PortNumber
+    deriving (Show,Eq)
+
 data SocksReply =
-	  SocksReplySuccess
-	| SocksReplyGeneralServerFailure
-	| SocksReplyConnectionNotAllowedByRule
-	| SocksReplyNetworkUnreachable
-	| SocksReplyHostUnreachable
-	| SocksReplyConnectionRefused
-	| SocksReplyTTLExpired
-	| SocksReplyCommandNotSupported
-	| SocksReplyAddrTypeNotSupported
-	| SocksReplyOther Word8
-	deriving (Show,Eq,Ord,Data,Typeable)
+      SocksReplySuccess
+    | SocksReplyError SocksError
+    deriving (Show,Eq,Ord,Data,Typeable)
 
-data SocksVersionNotSupported = SocksVersionNotSupported
-	deriving (Show,Data,Typeable)
+data SocksError =
+      SocksErrorGeneralServerFailure
+    | SocksErrorConnectionNotAllowedByRule
+    | SocksErrorNetworkUnreachable
+    | SocksErrorHostUnreachable
+    | SocksErrorConnectionRefused
+    | SocksErrorTTLExpired
+    | SocksErrorCommandNotSupported
+    | SocksErrorAddrTypeNotSupported
+    | SocksErrorOther Word8
+    deriving (Show,Eq,Ord,Data,Typeable)
 
-data SocksError = SocksError SocksReply
-	deriving (Show,Eq,Data,Typeable)
+data SocksVersionNotSupported = SocksVersionNotSupported
+    deriving (Show,Data,Typeable)
 
 instance Exception SocksError
 instance Exception SocksVersionNotSupported
 
 instance Enum SocksCommand where
-	toEnum 1 = SocksCommandConnect
-	toEnum 2 = SocksCommandBind
-	toEnum 3 = SocksCommandUdpAssociate
-	toEnum w
-		| w < 256   = SocksCommandOther $ fromIntegral w
-		| otherwise = error "socks command is only 8 bits"
-	fromEnum SocksCommandConnect      = 1
-	fromEnum SocksCommandBind         = 2
-	fromEnum SocksCommandUdpAssociate = 3
-	fromEnum (SocksCommandOther w)    = fromIntegral w
+    toEnum 1 = SocksCommandConnect
+    toEnum 2 = SocksCommandBind
+    toEnum 3 = SocksCommandUdpAssociate
+    toEnum w
+        | w < 256   = SocksCommandOther $ fromIntegral w
+        | otherwise = error "socks command is only 8 bits"
+    fromEnum SocksCommandConnect      = 1
+    fromEnum SocksCommandBind         = 2
+    fromEnum SocksCommandUdpAssociate = 3
+    fromEnum (SocksCommandOther w)    = fromIntegral w
 
 instance Enum SocksMethod where
-	toEnum 0    = SocksMethodNone
-	toEnum 1    = SocksMethodGSSAPI
-	toEnum 2    = SocksMethodUsernamePassword
-	toEnum 0xff = SocksMethodNotAcceptable
-	toEnum w
-		| w < 256   = SocksMethodOther $ fromIntegral w
-		| otherwise = error "socks method is only 8 bits"
-	fromEnum SocksMethodNone             = 0
-	fromEnum SocksMethodGSSAPI           = 1
-	fromEnum SocksMethodUsernamePassword = 2
-	fromEnum (SocksMethodOther w)        = fromIntegral w
-	fromEnum SocksMethodNotAcceptable    = 0xff
+    toEnum 0    = SocksMethodNone
+    toEnum 1    = SocksMethodGSSAPI
+    toEnum 2    = SocksMethodUsernamePassword
+    toEnum 0xff = SocksMethodNotAcceptable
+    toEnum w
+        | w < 256   = SocksMethodOther $ fromIntegral w
+        | otherwise = error "socks method is only 8 bits"
+    fromEnum SocksMethodNone             = 0
+    fromEnum SocksMethodGSSAPI           = 1
+    fromEnum SocksMethodUsernamePassword = 2
+    fromEnum (SocksMethodOther w)        = fromIntegral w
+    fromEnum SocksMethodNotAcceptable    = 0xff
 
+instance Enum SocksError where
+    fromEnum SocksErrorGeneralServerFailure       = 1
+    fromEnum SocksErrorConnectionNotAllowedByRule = 2
+    fromEnum SocksErrorNetworkUnreachable         = 3
+    fromEnum SocksErrorHostUnreachable            = 4
+    fromEnum SocksErrorConnectionRefused          = 5
+    fromEnum SocksErrorTTLExpired                 = 6
+    fromEnum SocksErrorCommandNotSupported        = 7
+    fromEnum SocksErrorAddrTypeNotSupported       = 8
+    fromEnum (SocksErrorOther w)                  = fromIntegral w
+    toEnum 1 = SocksErrorGeneralServerFailure
+    toEnum 2 = SocksErrorConnectionNotAllowedByRule
+    toEnum 3 = SocksErrorNetworkUnreachable
+    toEnum 4 = SocksErrorHostUnreachable
+    toEnum 5 = SocksErrorConnectionRefused
+    toEnum 6 = SocksErrorTTLExpired
+    toEnum 7 = SocksErrorCommandNotSupported
+    toEnum 8 = SocksErrorAddrTypeNotSupported
+    toEnum w = SocksErrorOther $ fromIntegral w
+
 instance Enum SocksReply where
-	fromEnum SocksReplySuccess                    = 0
-	fromEnum SocksReplyGeneralServerFailure       = 1
-	fromEnum SocksReplyConnectionNotAllowedByRule = 2
-	fromEnum SocksReplyNetworkUnreachable         = 3
-	fromEnum SocksReplyHostUnreachable            = 4
-	fromEnum SocksReplyConnectionRefused          = 5
-	fromEnum SocksReplyTTLExpired                 = 6
-	fromEnum SocksReplyCommandNotSupported        = 7
-	fromEnum SocksReplyAddrTypeNotSupported       = 8
-	fromEnum (SocksReplyOther w)                  = fromIntegral w
-	toEnum 0 = SocksReplySuccess
-	toEnum 1 = SocksReplyGeneralServerFailure
-	toEnum 2 = SocksReplyConnectionNotAllowedByRule
-	toEnum 3 = SocksReplyNetworkUnreachable
-	toEnum 4 = SocksReplyHostUnreachable
-	toEnum 5 = SocksReplyConnectionRefused
-	toEnum 6 = SocksReplyTTLExpired
-	toEnum 7 = SocksReplyCommandNotSupported
-	toEnum 8 = SocksReplyAddrTypeNotSupported
-	toEnum w
-		| w < 256   = SocksReplyOther $ fromIntegral w
-		| otherwise = error "sock reply is only 8 bits"
+    fromEnum SocksReplySuccess                    = 0
+    fromEnum (SocksReplyError e)                  = fromEnum e
+    toEnum 0 = SocksReplySuccess
+    toEnum n = SocksReplyError (toEnum n)
diff --git a/Network/Socks5/Wire.hs b/Network/Socks5/Wire.hs
--- a/Network/Socks5/Wire.hs
+++ b/Network/Socks5/Wire.hs
@@ -6,11 +6,11 @@
 -- Stability   : experimental
 -- Portability : unknown
 module Network.Socks5.Wire
-	( SocksHello(..)
-	, SocksHelloResponse(..)
-	, SocksRequest(..)
-	, SocksResponse(..)
-	) where
+    ( SocksHello(..)
+    , SocksHelloResponse(..)
+    , SocksRequest(..)
+    , SocksResponse(..)
+    ) where
 
 import Control.Applicative
 import Control.Monad
@@ -21,23 +21,28 @@
 
 import Network.Socks5.Types
 
+-- | Initial message sent by client with the list of authentification methods supported
 data SocksHello = SocksHello { getSocksHelloMethods :: [SocksMethod] }
-	deriving (Show,Eq)
+    deriving (Show,Eq)
 
+-- | Initial message send by server in return from Hello, with the
+-- server chosen method of authentication
 data SocksHelloResponse = SocksHelloResponse { getSocksHelloResponseMethod :: SocksMethod }
-	deriving (Show,Eq)
+    deriving (Show,Eq)
 
+-- | Define a SOCKS requests
 data SocksRequest = SocksRequest
-	{ requestCommand  :: SocksCommand
-	, requestDstAddr  :: SocksAddr
-	, requestDstPort  :: PortNumber
-	} deriving (Show,Eq)
+    { requestCommand  :: SocksCommand
+    , requestDstAddr  :: SocksHostAddress
+    , requestDstPort  :: PortNumber
+    } deriving (Show,Eq)
 
+-- | Define a SOCKS response
 data SocksResponse = SocksResponse
-	{ responseReply    :: SocksReply
-	, responseBindAddr :: SocksAddr
-	, responseBindPort :: PortNumber
-	} deriving (Show,Eq)
+    { responseReply    :: SocksReply
+    , responseBindAddr :: SocksHostAddress
+    , responseBindPort :: PortNumber
+    } deriving (Show,Eq)
 
 getAddr 1 = SocksAddrIPV4 <$> getWord32be
 getAddr 3 = SocksAddrDomainName <$> (getWord8 >>= getByteString . fromIntegral)
@@ -49,58 +54,57 @@
 putAddr (SocksAddrIPV6 (a,b,c,d)) = putWord8 4 >> mapM_ putWord32host [a,b,c,d]
 
 getSocksRequest 5 = do
-	cmd <- toEnum . fromIntegral <$> getWord8
-	_   <- getWord8
-	addr <- getWord8 >>= getAddr
-	port <- fromIntegral <$> getWord16be
-	return $ SocksRequest cmd addr port
+    cmd <- toEnum . fromIntegral <$> getWord8
+    _   <- getWord8
+    addr <- getWord8 >>= getAddr
+    port <- fromIntegral <$> getWord16be
+    return $ SocksRequest cmd addr port
 getSocksRequest v =
-	error ("unsupported version of the protocol " ++ show v)
+    error ("unsupported version of the protocol " ++ show v)
 
 getSocksResponse 5 = do
-	reply <- toEnum . fromIntegral <$> getWord8
-	_     <- getWord8
-	addr <- getWord8 >>= getAddr
-	port <- fromIntegral <$> getWord16be
-	return $ SocksResponse reply addr port
+    reply <- toEnum . fromIntegral <$> getWord8
+    _     <- getWord8
+    addr <- getWord8 >>= getAddr
+    port <- fromIntegral <$> getWord16be
+    return $ SocksResponse reply addr port
 getSocksResponse v =
-	error ("unsupported version of the protocol " ++ show v)
+    error ("unsupported version of the protocol " ++ show v)
 
 instance Serialize SocksHello where
-	put (SocksHello ms) = do
-		putWord8 5
-		putWord8 $ fromIntegral $ length ms
-		mapM_ (putWord8 . fromIntegral . fromEnum) ms
-	get = do
-		v <- getWord8
-		case v of
-			5 -> getWord8 >>= flip replicateM (toEnum . fromIntegral <$> getWord8) . fromIntegral >>= return . SocksHello
-			_ -> error "unsupported sock hello version"
+    put (SocksHello ms) = do
+        putWord8 5
+        putWord8 $ fromIntegral $ length ms
+        mapM_ (putWord8 . fromIntegral . fromEnum) ms
+    get = do
+        v <- getWord8
+        case v of
+            5 -> getWord8 >>= flip replicateM (toEnum . fromIntegral <$> getWord8) . fromIntegral >>= return . SocksHello
+            _ -> error "unsupported sock hello version"
 
 instance Serialize SocksHelloResponse where
-	put (SocksHelloResponse m) = putWord8 5 >> putWord8 (fromIntegral $ fromEnum $ m)
-	get = do
-		v <- getWord8
-		case v of
-			5 -> SocksHelloResponse . toEnum . fromIntegral <$> getWord8
-			_ -> error "unsupported sock hello response version"
+    put (SocksHelloResponse m) = putWord8 5 >> putWord8 (fromIntegral $ fromEnum $ m)
+    get = do
+        v <- getWord8
+        case v of
+            5 -> SocksHelloResponse . toEnum . fromIntegral <$> getWord8
+            _ -> error "unsupported sock hello response version"
 
 instance Serialize SocksRequest where
-	put req = do
-		putWord8 5
-		putWord8 $ fromIntegral $ fromEnum $ requestCommand req
-		putWord8 0
-		putAddr $ requestDstAddr req
-		putWord16be $ fromIntegral $ requestDstPort req
-		
-	get = getWord8 >>= getSocksRequest
+    put req = do
+        putWord8 5
+        putWord8 $ fromIntegral $ fromEnum $ requestCommand req
+        putWord8 0
+        putAddr $ requestDstAddr req
+        putWord16be $ fromIntegral $ requestDstPort req
+        
+    get = getWord8 >>= getSocksRequest
 
 instance Serialize SocksResponse where
-	put req = do
-		putWord8 5
-		putWord8 $ fromIntegral $ fromEnum $ responseReply req
-		putWord8 0
-		putAddr $ responseBindAddr req
-		putWord16be $ fromIntegral $ responseBindPort req
-	get = getWord8 >>= getSocksResponse
-
+    put req = do
+        putWord8 5
+        putWord8 $ fromIntegral $ fromEnum $ responseReply req
+        putWord8 0
+        putAddr $ responseBindAddr req
+        putWord16be $ fromIntegral $ responseBindPort req
+    get = getWord8 >>= getSocksResponse
diff --git a/socks.cabal b/socks.cabal
--- a/socks.cabal
+++ b/socks.cabal
@@ -1,5 +1,5 @@
 Name:                socks
-Version:             0.4.2
+Version:             0.5.0
 Description:         Socks proxy (version 5) implementation.
 License:             BSD3
 License-file:        LICENSE
@@ -20,8 +20,10 @@
                    , cereal >= 0.3.1
                    , network >= 2.3
   Exposed-modules:   Network.Socks5
-  Other-modules:     Network.Socks5.Wire
+                     Network.Socks5.Lowlevel
                      Network.Socks5.Types
+  Other-modules:     Network.Socks5.Wire
+                     Network.Socks5.Conf
                      Network.Socks5.Command
   ghc-options:       -Wall -fno-warn-missing-signatures
 
