moesocks 0.1.0.21 → 0.1.0.22
raw patch · 11 files changed
+163/−95 lines, 11 files
Files
- CHANGELOG.md +5/−0
- README.md +27/−14
- moesocks.cabal +1/−1
- src/Network/MoeSocks/App.hs +12/−12
- src/Network/MoeSocks/Config.hs +1/−0
- src/Network/MoeSocks/Encrypt.hs +16/−10
- src/Network/MoeSocks/Helper.hs +26/−7
- src/Network/MoeSocks/Options.hs +9/−0
- src/Network/MoeSocks/TCP.hs +45/−32
- src/Network/MoeSocks/Type.hs +6/−4
- src/Network/MoeSocks/UDP.hs +15/−15
CHANGELOG.md view
@@ -1,3 +1,8 @@+0.1.0.22+--------+* Add the `-o` flag to turn on simple obfuscation (randomly flush socket to vary+ packet length). There is about a 10-20% performance cost.+ 0.1.0.21 -------- * Add in README that `GHC 7.10.2` is a must!
README.md view
@@ -3,12 +3,7 @@ A socks5 proxy using the client / server architecture. -MoeSocks is greatly inspired by [shadowsocks].--A sample `config.json` file is included in this repository and the cabal-archive.--type `moesocks --help` for help.+MoeSocks is greatly inspired by [shadowsocks] and can be used in place of it. Installation ------------@@ -21,26 +16,43 @@ * Repeat, you need `GHC 7.10.2` exactly, not `7.10.1` or anything else, since remote only runs well in `7.10.2`.+ * Update packages cabal update+ * Install cabal install moesocks +* Add `~/.cabal/bin` to your `$PATH`, if you haven't already.+ Usage -----+* Download a sample [config.json] to your current path+ * Edit `config.json` to fit your setup (at least the `remote` and `password` fields)+ * Start a remote node outside the firewall: moesocks --role remote -c config.json+ * Start a local node inside the firewall: moesocks --role local -c config.json+ * Now you have a socks5 proxy running inside the firewall using port `localPort`. +* Shadowsocks compatible obfuscation can be turned on with the `-o` flag to make+ statistical analysis on packet length a bit more confusing.++* See more options:++ moesocks --help++ Features -------- * Socks5 proxy service, obviously@@ -61,13 +73,14 @@ ------------------ * None -Notes--------There is an earlier implementation of [shadowsocks-haskell] by rnons that-makes MoeSocks possible. --The original goal of MoeSocks is to provide extra configurability to standard-shadowsocks, but it has been discarded since remote is too flaky. +Credits+-------+* [shadowsocks] greatly inspired MoeSocks.+ Shadowsocks introduced a ground breaking design and implementation to bypass+ Internet censorship in China. +* [shadowsocks-haskell] by rnons, another implementation of shadowsocks in + Haskell, also greatly inspired MoeSocks. Much of the understanding of+ the internal of shadowsocks was gained by reading rnons's implementation. License --------@@ -88,6 +101,6 @@ [shadowsocks]:https://github.com/shadowsocks/shadowsocks [shadowsocks-go]:https://github.com/shadowsocks/shadowsocks-go [shadowsocks-haskell]:https://github.com/rnons/shadowsocks-haskell-+[config.json]:https://raw.githubusercontent.com/nfjinjing/moesocks/master/config.json
moesocks.cabal view
@@ -1,6 +1,6 @@ name: moesocks category: Network-version: 0.1.0.21+version: 0.1.0.22 license: Apache-2.0 synopsis: A functional firewall killer description: A socks5 proxy using the client / server architecture.
src/Network/MoeSocks/App.hs view
@@ -21,7 +21,7 @@ import Network.MoeSocks.TCP import Network.MoeSocks.Type import Network.MoeSocks.UDP-import Network.MoeSocks.Encrypt (initBuilder)+import Network.MoeSocks.Encrypt (initCipherBox) import Network.Socket hiding (send, recv, recvFrom, sendTo) import Network.Socket.ByteString import Prelude hiding ((-), take)@@ -132,7 +132,7 @@ tell "\n\n" case _maybeFilePath of Just _filePath -> do- tell "Failed to parse configuration file"+ tell "Failed to parse configuration file: " tell _filePath tell "\n" tell "Example: \n"@@ -178,10 +178,12 @@ let _method = _config ^. method - _cipherBox <- (io - initBuilder _method (_config ^. password)) >>= \case+ _cipherBox <- (io - initCipherBox _method (_config ^. password)) >>= \case Nothing -> throwError - "Invalid method '" <> _method ^. _Text Just (a, b, c, d) -> pure - CipherBox a b c d+ + let _env = Env _options _config _cipherBox let localAppBuilder :: AppType -> String @@ -203,7 +205,7 @@ _s@(_newSocket, _newSockAddr) <- accept _socket setSocketCloseOnExec _newSocket -- send immediately!- {-setSocketOption _socket NoDelay 1 -}+ setSocketOption _socket NoDelay 1 forkIO - catchExceptAsyncLog "L TCP thread" - logSA "L TCP client socket" (pure _s) -@@ -228,7 +230,7 @@ let localSocks5App :: (Socket, SockAddr) -> IO () localSocks5App = localAppBuilder TCP_App "socks5" - - local_Socks5_RequestHandler _cipherBox _config+ local_Socks5_RequestHandler _env showForwarding :: Forward -> String showForwarding (Forward _localPort _remoteHost _remotePort) =@@ -244,16 +246,14 @@ forward_TCP_App _f _s = do let _m = showForwarding _f localAppBuilder TCP_App ("TCP forwarding " <> _m)- (local_TCP_ForwardRequestHandler _cipherBox - _config _f) + (local_TCP_ForwardRequestHandler _env _f) _s forward_UDP_App :: Forward -> (Socket, SockAddr) -> IO () forward_UDP_App _f _s = do let _m = showForwarding _f localAppBuilder UDP_App ("UDP forwarding " <> _m)- (local_UDP_ForwardRequestHandler _cipherBox- _config _f) + (local_UDP_ForwardRequestHandler _env _f) _s let remote_TCP_App :: (Socket, SockAddr) -> IO ()@@ -272,11 +272,11 @@ (_newSocket, _) <- accept _socket setSocketCloseOnExec _newSocket -- send immediately!- {-setSocketOption _socket NoDelay 1 -}+ setSocketOption _socket NoDelay 1 forkIO - catchExceptAsyncLog "R thread" - logSocket "R remote socket" (pure _newSocket) -- remote_TCP_RequestHandler _cipherBox _config + remote_TCP_RequestHandler _env forever - handleRemote _remoteSocket @@ -297,7 +297,7 @@ forkIO - catchExceptAsyncLog "R thread" - - remote_UDP_RequestHandler _cipherBox _config _msg _s+ remote_UDP_RequestHandler _env _msg _s
src/Network/MoeSocks/Config.hs view
@@ -30,5 +30,6 @@ , _forwardTCP = [] , _forwardUDP = [] , _disableSocks5 = False+ , _obfuscation = False , _params = [] }
src/Network/MoeSocks/Encrypt.hs view
@@ -27,6 +27,7 @@ -- BEGIN backports infixr 0 -+{-# INLINE (-) #-} (-) :: (a -> b) -> a -> b (-) = ($) @@ -36,9 +37,14 @@ type IV_Length = Int type Cipher = S.Maybe ByteString -> IO ByteString -type CipherBuilder = ByteString -> IO Cipher-type CipherBox = (Int, IO ByteString, CipherBuilder, CipherBuilder)+type IV = ByteString+type CipherBuilder = IV -> IO Cipher +type EncryptBuilder = CipherBuilder+type DecryptBuilder = CipherBuilder++type CipherBox = (IV_Length, IO IV, EncryptBuilder, DecryptBuilder)+ type MaybeT_IO = ExceptT () IO methods :: Map Text (KeyLength, IV_Length)@@ -59,7 +65,7 @@ ] -hashKey :: ByteString -> Int -> Int -> ByteString+hashKey :: ByteString -> KeyLength -> IV_Length -> ByteString hashKey aPassword aKeyLen a_IV_len = loop mempty mempty where _stopLength = aKeyLen + a_IV_len@@ -90,17 +96,17 @@ identityCipher :: Cipher identityCipher = pure . S.fromMaybe mempty -initBuilder :: Text -> Text -> IO (Maybe CipherBox)-initBuilder aMethod aPassword +initCipherBox :: Text -> Text -> IO (Maybe CipherBox)+initCipherBox aMethod aPassword | aMethod == "none" = let constCipher = const - pure identityCipher in pure - Just (0, pure mempty, constCipher, constCipher) | otherwise =- fmap eitherToMaybe - runExceptT - initBuilder' aMethod aPassword+ fmap eitherToMaybe - runExceptT - initCipherBox' aMethod aPassword -initBuilder' :: Text -> Text -> MaybeT_IO CipherBox-initBuilder' aMethod aPassword = do+initCipherBox' :: Text -> Text -> MaybeT_IO CipherBox+initCipherBox' aMethod aPassword = do _method <- mio - ssl - getCipherByName - aMethod ^. _Text (_keyLength, _IV_Length) <- getMaybe - methods ^? ix aMethod@@ -108,7 +114,7 @@ let _hashed = hashKey (review utf8 aPassword) _keyLength _IV_Length _IV_Maker = ssl - randBytes _IV_Length - _encryptBuilder :: ByteString -> IO Cipher+ _encryptBuilder :: CipherBuilder _encryptBuilder _iv = do _ctx <- ssl - E.cipherInitBS _method _hashed _iv Encrypt @@ -120,7 +126,7 @@ pure _encrypt - _decryptBuilder :: ByteString -> IO Cipher+ _decryptBuilder :: CipherBuilder _decryptBuilder _iv = do _ctx <- ssl - E.cipherInitBS _method _hashed _iv Decrypt
src/Network/MoeSocks/Helper.hs view
@@ -24,6 +24,7 @@ import Prelude hiding (take, (-)) import System.IO.Unsafe (unsafePerformIO) import System.Log.Logger+import System.Random import System.Posix.IO (FdOption(CloseOnExec), setFdOption) import System.Timeout (timeout) import qualified Data.ByteString as S@@ -35,8 +36,9 @@ -- BEGIN backports infixr 0 -+{-# INLINE (-) #-} (-) :: (a -> b) -> a -> b-(-) = ($)+f - x = f x -- END backports @@ -211,8 +213,8 @@ setSocketCloseOnExec _socket -- send immediately!- {-when (aSocketType == Stream) --}- {-setSocketOption _socket NoDelay 1 -}+ when (aSocketType == Stream) -+ setSocketOption _socket NoDelay 1 puts - "Getting socket: " <> show addrInfo {-puts - "Socket family: " <> show family-}@@ -254,6 +256,19 @@ send_ :: Socket -> ByteString -> IO () send_ = sendAll +sendAllRandom :: Socket -> ByteString -> IO ()+sendAllRandom aSocket aBuffer = do+ _loop aBuffer+ where+ _lengthUpperBound = 4096+ _loop _buffer = do+ _randomLength <- randomRIO (0, _lengthUpperBound)+ {-_say - "randomLength: " <> show _randomLength-}+ let (_thisBuffer, _nextBuffer) = S.splitAt _randomLength _buffer+ sendAll aSocket _thisBuffer+ when (_nextBuffer & isn't _Empty) - do+ yield+ _loop _nextBuffer sendBytes :: HQueue -> ByteString -> IO () sendBytes _queue = atomically . writeTBQueue _queue . S.Just@@ -315,7 +330,7 @@ _produce :: Int -> IO () _produce _bytesReceived = flip onException (f S.Nothing) - do _r <- timeoutFor aID aTimeout - recv_ aSocket- {-pute - "Get chunk: " <> (show - S.length _r) -- <> " " <> aID-}+ {-puts - "Get chunk: " <> (show - S.length _r) -- <> " " <> aID-} if (_r & isn't _Empty) then do forM_ aThrottle - \_throttle -> do@@ -351,8 +366,8 @@ consumeLoop :: String -> Timeout -> Maybe Double ->- Socket -> HQueue -> IO ()-consumeLoop aID aTimeout aThrottle aSocket aTBQueue = do+ Socket -> HQueue -> Bool -> IO ()+consumeLoop aID aTimeout aThrottle aSocket aTBQueue randomize = do _startTime <- getCurrentTime @@ -388,8 +403,12 @@ case _newPacket of S.Nothing -> () <$ _shutdown S.Just _data -> do+ let _send = if randomize + then sendAllRandom+ else sendAll+ timeoutFor aID aTimeout - - sendAll aSocket _data+ _send aSocket _data yield _consume - _allBytesSent + S.length _data
src/Network/MoeSocks/Options.hs view
@@ -116,6 +116,14 @@ & view (from _Text)) "timeout connection in seconds" + _obfuscation :: O.Parser Bool + _obfuscation = switch -+ short 'o'+ <> long "obfuscation"+ <> help ("Turn on simple obfuscation while still "+ <> "being compatible with "+ <> "shadowsocks protocol, at the cost of "+ <> "about 10-20% performance degradation.") _verbosity :: O.Parser Priority _verbosity = flag INFO DEBUG -@@ -210,6 +218,7 @@ <*> fmap parseForwarding _forwardTCP <*> fmap parseForwarding _forwardUDP <*> _disableSocks5+ <*> _obfuscation <*> _params opts :: ParserInfo MoeOptions
src/Network/MoeSocks/TCP.hs view
@@ -19,12 +19,11 @@ import Prelude hiding ((-), take) import qualified Data.Strict as S -local_Socks5_RequestHandler :: CipherBox - -> MoeConfig +local_Socks5_RequestHandler :: Env -> ByteString -> (Socket, SockAddr) -> IO ()-local_Socks5_RequestHandler aCipherBox aConfig _ (aSocket,_) = do+local_Socks5_RequestHandler aEnv _ (aSocket,_) = do (_partialBytesAfterGreeting, _r) <- parseSocket "clientGreeting" mempty identityCipher greetingParser aSocket@@ -42,38 +41,41 @@ connectionParser aSocket - local_TCP_RequestHandler aCipherBox aConfig _parsedRequest True aSocket+ local_TCP_RequestHandler aEnv _parsedRequest True aSocket -local_TCP_ForwardRequestHandler :: CipherBox - -> MoeConfig +local_TCP_ForwardRequestHandler :: Env -> Forward -> ByteString -> (Socket, SockAddr) -> IO ()-local_TCP_ForwardRequestHandler aCipherBox aConfig aForwarding _ (aSocket,_) = do+local_TCP_ForwardRequestHandler aEnv aForwarding _ (aSocket,_) = + do let _clientRequest = ClientRequest TCP_IP_stream_connection (Domain_name - aForwarding ^. forwardRemoteHost) (aForwarding ^. forwardRemotePort) - local_TCP_RequestHandler aCipherBox aConfig (mempty, _clientRequest) False aSocket+ local_TCP_RequestHandler aEnv+ (mempty, _clientRequest) False aSocket -local_TCP_RequestHandler :: CipherBox - -> MoeConfig +local_TCP_RequestHandler :: Env -> (ByteString, ClientRequest) -> Bool -> Socket -> IO ()-local_TCP_RequestHandler aCipherBox aConfig +local_TCP_RequestHandler aEnv (_partialBytesAfterClientRequest, _clientRequest) shouldReplyClient aSocket = do let - _c = aConfig + _c = aEnv ^. config + _cipherBox = aEnv ^. cipherBox+ _obfuscation = aEnv ^. options . obfuscation+ _initSocket = getSocket (_c ^. remote) (_c ^. remotePort) Stream @@ -97,20 +99,20 @@ _log - "L T: " <> _msg let handleLocal __remoteSocket = do- _encodeIV <- aCipherBox ^. generateIV - _encrypt <- aCipherBox ^. encryptBuilder - _encodeIV+ _encodeIV <- _cipherBox ^. generateIV + _encrypt <- _cipherBox ^. encryptBuilder - _encodeIV let _header = shadowSocksRequestBuilder _clientRequest - sendChannel <- newTBQueueIO - aConfig ^. tcpBufferSize- receiveChannel <- newTBQueueIO - aConfig ^. tcpBufferSize+ sendChannel <- newTBQueueIO - _c ^. tcpBufferSize+ receiveChannel <- newTBQueueIO - _c ^. tcpBufferSize let _logId x = x <> " " <> _msg- _timeout = aConfig ^. timeout * 1000 * 1000+ _timeout = _c ^. timeout * 1000 * 1000 _throttle = - if aConfig ^. throttle- then Just - aConfig ^. throttleSpeed+ if _c ^. throttle+ then Just - _c ^. throttleSpeed else Nothing let sendThread = do@@ -135,6 +137,7 @@ _throttle __remoteSocket sendChannel+ _obfuscation finally ( connectMarket (Just - _logId "L --> +", _produce)@@ -143,8 +146,8 @@ pure () let receiveThread = do- _decodeIV <- recv __remoteSocket (aCipherBox ^. ivLength)- _decrypt <- aCipherBox ^. decryptBuilder - _decodeIV+ _decodeIV <- recv __remoteSocket (_cipherBox ^. ivLength)+ _decrypt <- _cipherBox ^. decryptBuilder - _decodeIV let _produce = produceLoop (_logId "L <-- + Loop") _timeout@@ -159,6 +162,7 @@ _NoThrottle aSocket receiveChannel+ False finally ( connectMarket (Just - _logId "L <-- +", _produce)@@ -174,11 +178,16 @@ handleLocal _remoteSocket -remote_TCP_RequestHandler :: CipherBox -> MoeConfig -> Socket -> IO ()-remote_TCP_RequestHandler aCipherBox aConfig aSocket = do- _decodeIV <- recv aSocket (aCipherBox ^. ivLength)- _decrypt <- aCipherBox ^. decryptBuilder - _decodeIV+remote_TCP_RequestHandler :: Env -> Socket -> IO ()+remote_TCP_RequestHandler aEnv aSocket = do+ let+ _obfuscation = aEnv ^. options . obfuscation+ _cipherBox = aEnv ^. cipherBox+ _c = aEnv ^. config + _decodeIV <- recv aSocket (_cipherBox ^. ivLength)+ _decrypt <- _cipherBox ^. decryptBuilder - _decodeIV+ (_leftOverBytes, _clientRequest) <- parseSocket "clientRequest" mempty@@ -204,15 +213,17 @@ let handleTarget __leftOverBytes __targetSocket = do- sendChannel <- newTBQueueIO - aConfig ^. tcpBufferSize- receiveChannel <- newTBQueueIO - aConfig ^. tcpBufferSize+ sendChannel <- newTBQueueIO - _c ^. tcpBufferSize+ receiveChannel <- newTBQueueIO - _c ^. tcpBufferSize let _logId x = x <> " " <> _msg- _timeout = aConfig ^. timeout * 1000 * 1000+ -- let remote wait slightly longer, so local can timeout+ -- and disconnect+ _timeout = (_c ^. timeout + 30) * 1000 * 1000 _throttle = - if aConfig ^. throttle- then Just - aConfig ^. throttleSpeed+ if _c ^. throttle+ then Just - _c ^. throttleSpeed else Nothing let sendThread = do@@ -232,6 +243,7 @@ _NoThrottle __targetSocket sendChannel+ False finally (@@ -241,8 +253,8 @@ pure () let receiveThread = do- _encodeIV <- aCipherBox ^. generateIV - _encrypt <- aCipherBox ^. encryptBuilder - _encodeIV+ _encodeIV <- _cipherBox ^. generateIV + _encrypt <- _cipherBox ^. encryptBuilder - _encodeIV sendBytes receiveChannel _encodeIV let _produce = do@@ -260,6 +272,7 @@ _throttle aSocket receiveChannel+ _obfuscation finally (
src/Network/MoeSocks/Type.hs view
@@ -93,6 +93,7 @@ , _forwardTCP :: [Forward] , _forwardUDP :: [Forward] , _disableSocks5 :: Bool+ , _obfuscation :: Bool , _params :: [(Text, Value)] } deriving (Show, Eq)@@ -100,19 +101,20 @@ makeLenses ''MoeOptions type Cipher = S.Maybe ByteString -> IO ByteString -type CipherBuilder = ByteString -> IO Cipher+type IV = ByteString+type CipherBuilder = IV -> IO Cipher data CipherBox = CipherBox { _ivLength :: Int- , _generateIV :: IO ByteString+ , _generateIV :: IO IV , _encryptBuilder :: CipherBuilder , _decryptBuilder :: CipherBuilder } makeLenses '' CipherBox -{-+ data Env = Env { _options :: MoeOptions@@ -121,7 +123,7 @@ } makeLenses ''Env--}+
src/Network/MoeSocks/UDP.hs view
@@ -37,16 +37,16 @@ processAll f x = (<>) <$> f (S.Just x) <*> f S.Nothing -local_UDP_ForwardRequestHandler :: CipherBox - -> MoeConfig +local_UDP_ForwardRequestHandler :: Env -> Forward -> ByteString -> (Socket,SockAddr) -> IO ()-local_UDP_ForwardRequestHandler aCipherBox aConfig aForwarding aMessage +local_UDP_ForwardRequestHandler aEnv aForwarding aMessage (aSocket, aSockAddr) = do - let _c = aConfig+ let _c = aEnv ^. config+ _cipherBox = _cipherBox let _clientRequest = ClientRequest UDP_port@@ -62,8 +62,8 @@ \(_remoteSocket, _remoteAddr) -> do connect _remoteSocket _remoteAddr - _encodeIV <- aCipherBox ^. generateIV - _encrypt <- aCipherBox ^. encryptBuilder - _encodeIV+ _encodeIV <- _cipherBox ^. generateIV + _encrypt <- _cipherBox ^. encryptBuilder - _encodeIV {-let (_encrypt, _decrypt) = (pure, pure)-} @@ -80,9 +80,9 @@ _response <- recv_ _remoteSocket - let (_decodeIV, _responseMsg) = S.splitAt (aCipherBox ^. ivLength) + let (_decodeIV, _responseMsg) = S.splitAt (_cipherBox ^. ivLength) _response- _decrypt <- aCipherBox ^. decryptBuilder - _decodeIV+ _decrypt <- _cipherBox ^. decryptBuilder - _decodeIV (_r, _) <- processAll _decrypt _responseMsg >>= parseShadowSocksRequest@@ -91,16 +91,16 @@ sendAllTo aSocket _r aSockAddr -remote_UDP_RequestHandler :: CipherBox - -> MoeConfig +remote_UDP_RequestHandler :: Env -> ByteString -> (Socket, SockAddr) -> IO ()-remote_UDP_RequestHandler aCipherBox _ aMessage (aSocket, aSockAddr) = do- let (_decodeIV, _eMsg) = S.splitAt (aCipherBox ^. ivLength) +remote_UDP_RequestHandler aEnv aMessage (aSocket, aSockAddr) = do+ let _cipherBox = aEnv ^. cipherBox+ let (_decodeIV, _eMsg) = S.splitAt (_cipherBox ^. ivLength) aMessage - _decrypt <- aCipherBox ^. decryptBuilder - _decodeIV+ _decrypt <- _cipherBox ^. decryptBuilder - _decodeIV _msg <- processAll _decrypt _eMsg (_decryptedMessage, _clientRequest) <- parseShadowSocksRequest _msg@@ -125,8 +125,8 @@ {-puts - "R UDP <--: " <> show _r-} when (_r & isn't _Empty) - do- _encodeIV <- aCipherBox ^. generateIV - _encrypt <- aCipherBox ^. encryptBuilder - _encodeIV+ _encodeIV <- _cipherBox ^. generateIV + _encrypt <- _cipherBox ^. encryptBuilder - _encodeIV _encryptedMessage <- processAll _encrypt _r sendAllTo aSocket (_encodeIV <> _encryptedMessage) aSockAddr