packages feed

socks (empty) → 0.1.0

raw patch · 9 files changed

+466/−0 lines, 9 filesdep +basedep +bytestringdep +cerealsetup-changed

Dependencies added: base, bytestring, cereal, network

Files

+ Example.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE OverloadedStrings #-}+import Network.Socks5+import Network.Socket hiding (recv)+import Network.Socket.ByteString+import Network.BSD+import Data.ByteString.Char8 ()++import System.Environment (getArgs)++main = do+	args <- getArgs+	let destinationName = case args of+		[]    -> "www.google.com"+		(x:_) -> x+	-- socks server is expected to be running on localhost port 1080+	he     <- getHostByName "localhost"+	let socksServerAddr = SockAddrInet 1080 (head $ hostAddresses he)++	example1 socksServerAddr destinationName+	example2 socksServerAddr destinationName+	where+		-- connect to @destName on port 80 through the socks server+		-- www.google.com get resolve on the client here and then the sockaddr is+		-- passed to socksConnectAddr+		example1 socksServerAddr destName = do+			socket <- socket AF_INET Stream defaultProtocol+			+			gHost <- getHostByName destName+			let destinationAddr = SockAddrInet 80 (head $ hostAddresses gHost)+			socksConnectAddr socket socksServerAddr destinationAddr++			sendAll socket "GET / HTTP/1.0\r\n\r\n"+			recv socket 4096 >>= putStrLn . show+			sClose socket+		-- connect to @destName on port 80 through the socks server+		-- the server is doing the resolution itself+		example2 socksServerAddr destName = do+			socket <- socket AF_INET Stream defaultProtocol+			socksConnectName socket socksServerAddr destName 80+			sendAll socket "GET / HTTP/1.0\r\n\r\n"+			recv socket 4096 >>= putStrLn . show+			sClose socket
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) 2010-2011 Vincent Hanquez <vincent@snarc.org>++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ Network/Socks5.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ViewPatterns #-}+-- |+-- Module      : Network.Socks5+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : experimental+-- Portability : unknown+--+module Network.Socks5+	( socksConnectAddr+	, socksConnectName+	) where++import Control.Monad+import Network.Socket (Socket, SockAddr(..), PortNumber, connect)+import Network.Socks5.Command+import Network.Socks5.Types++withSocks socket sockaddr f = do+	connect socket sockaddr+	r <- socks5Establish socket [SocksMethodNone]+	when (r == SocksMethodNotAcceptable) $ error "cannot connect with no socks method of authentication"+	f++-- | 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.+--+-- a unix sockaddr will raises an exception.+--+-- |socket|-----sockServer----->|server|----destAddr----->|destination|+socksConnectAddr :: Socket -> SockAddr -> SockAddr -> IO ()+socksConnectAddr socket sockserver destaddr = withSocks socket sockserver $ do+	case destaddr of+		SockAddrInet p h      -> socks5ConnectIPV4 socket h p >> return ()+		SockAddrInet6 p _ h _ -> socks5ConnectIPV6 socket h p >> return ()+		SockAddrUnix _        -> error "unsupported unix sockaddr type"++-- | 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 socket sockserver destination port = withSocks socket sockserver $ do+	_ <- socks5ConnectDomainName socket destination port+	return ()
+ Network/Socks5/Command.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ViewPatterns #-}+-- |+-- Module      : Network.Socks5.Command+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : experimental+-- Portability : unknown+--+module Network.Socks5.Command+	( socks5Establish+	, socks5ConnectIPV4+	, socks5ConnectIPV6+	, socks5ConnectDomainName+	-- * lowlevel interface+	, socks5Rpc+	) where++import Control.Applicative+import Control.Exception+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import Data.Serialize++import Network.Socket (Socket, PortNumber, HostAddress, HostAddress6)+import Network.Socket.ByteString++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)++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+			}++		onReply (SocksAddrIPV4 h, p) = (h, p)+		onReply _                    = error "ipv4 requested, got something different"++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"++-- 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+	}++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)++runGetDone :: Show 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      = putStrLn (show a) >> return a
+ Network/Socks5/Types.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE DeriveDataTypeable #-}+-- |+-- Module      : Network.Socks5.Types+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : experimental+-- Portability : unknown+module Network.Socks5.Types+	( SocksCommand(..)+	, SocksMethod(..)+	, SocksAddr(..)+	, SocksReply(..)+	, SocksVersionNotSupported(..)+	, SocksError(..)+	) where++import Data.ByteString (ByteString)+import Data.Word+import Data.Data+import Network.Socket (HostAddress, HostAddress6)+import Control.Exception++data SocksCommand =+	  SocksCommandConnect+	| SocksCommandBind+	| SocksCommandUdpAssociate+	| SocksCommandOther Word8+	deriving (Show,Eq,Ord)++data SocksMethod =+	  SocksMethodNone+	| SocksMethodGSSAPI+	| SocksMethodUsernamePassword+	| SocksMethodOther Word8+	| SocksMethodNotAcceptable+	deriving (Show,Eq,Ord)++data SocksAddr =+	  SocksAddrIPV4 HostAddress+	| SocksAddrDomainName ByteString+	| SocksAddrIPV6 HostAddress6+	deriving (Show,Eq)++data SocksReply =+	  SocksReplySuccess+	| SocksReplyGeneralServerFailure+	| SocksReplyConnectionNotAllowedByRule+	| SocksReplyNetworkUnreachable+	| SocksReplyHostUnreachable+	| SocksReplyConnectionRefused+	| SocksReplyTTLExpired+	| SocksReplyCommandNotSupported+	| SocksReplyAddrTypeNotSupported+	| SocksReplyOther Word8+	deriving (Show,Eq,Ord,Data,Typeable)++data SocksVersionNotSupported = SocksVersionNotSupported+	deriving (Show,Data,Typeable)++data SocksError = SocksError SocksReply+	deriving (Show,Eq,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++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++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"
+ Network/Socks5/Wire.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE DeriveDataTypeable #-}+-- |+-- Module      : Network.Socks5.Wire+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : experimental+-- Portability : unknown+module Network.Socks5.Wire+	( SocksHello(..)+	, SocksHelloResponse(..)+	, SocksRequest(..)+	, SocksResponse(..)+	) where++import Control.Applicative+import Control.Monad+import qualified Data.ByteString as B+import Data.Serialize++import Network.Socket (PortNumber)++import Network.Socks5.Types++data SocksHello = SocksHello { getSocksHelloMethods :: [SocksMethod] }+	deriving (Show,Eq)++data SocksHelloResponse = SocksHelloResponse { getSocksHelloResponseMethod :: SocksMethod }+	deriving (Show,Eq)++data SocksRequest = SocksRequest+	{ requestCommand  :: SocksCommand+	, requestDstAddr  :: SocksAddr+	, requestDstPort  :: PortNumber+	} deriving (Show,Eq)++data SocksResponse = SocksResponse+	{ responseReply    :: SocksReply+	, responseBindAddr :: SocksAddr+	, responseBindPort :: PortNumber+	} deriving (Show,Eq)++getAddr 1 = SocksAddrIPV4 <$> getWord32be+getAddr 3 = SocksAddrDomainName <$> (getWord8 >>= getByteString . fromIntegral)+getAddr 4 = SocksAddrIPV6 <$> (liftM4 (,,,) getWord32le getWord32le getWord32le getWord32le)+getAddr n = error ("cannot get unknown socket address type: " ++ show n)++putAddr (SocksAddrIPV4 h)         = putWord8 1 >> putWord32host h+putAddr (SocksAddrDomainName b)   = putWord8 3 >> putWord8 (fromIntegral $ B.length b) >> putByteString b+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+getSocksRequest 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+getSocksResponse 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"++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"++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++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+
+ README.md view
@@ -0,0 +1,21 @@+Socks+=====++Haskell library implementation of the SOCKS 5 protocol.++TODO+----++ * more socks authentification methods: only no authentification is supported for now.+ * support of socks' bind for server to client connection (like FTP).+ * add socks4a and socks4 support.++Usage+-----++See Example.hs for really simple and straighforward example. The main api is only 2 calls:++ * socksConnectAddr which connect to a SockAddr (SockAddrInet or SockAddrInet6).+   The name resolution is left on client side.+ * socksConnectName which connect to a fully qualified domain name "www.example.com".+   The proxy server will do the name resolution.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ socks.cabal view
@@ -0,0 +1,26 @@+Name:                socks+Version:             0.1.0+Description:         Socks proxy (version 5) implementation.+License:             BSD3+License-file:        LICENSE+Copyright:           Vincent Hanquez <vincent@snarc.org>+Author:              Vincent Hanquez <vincent@snarc.org>+Maintainer:          Vincent Hanquez <vincent@snarc.org>+Synopsis:            Socks proxy (version 5) implementation.+Build-Type:          Simple+Category:            Network+stability:           experimental+Cabal-Version:       >=1.6+Homepage:            http://github.com/vincenthz/hs-socks+data-files:          README.md, Example.hs++Library+  Build-Depends:     base >= 3 && < 5+                   , bytestring+                   , cereal >= 0.3+                   , network >= 2.3+  Exposed-modules:   Network.Socks5+  Other-modules:     Network.Socks5.Wire+                     Network.Socks5.Types+                     Network.Socks5.Command+  ghc-options:       -Wall -fno-warn-missing-signatures