packages feed

moesocks 0.1.0.21 → 0.1.0.22

raw patch · 11 files changed

+163/−95 lines, 11 files

Files

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