packages feed

moesocks 0.1.0.17 → 0.1.0.18

raw patch · 11 files changed

+265/−142 lines, 11 files

Files

CHANGELOG.md view
@@ -1,6 +1,10 @@+0.1.0.18+--------+* Add complete command line arguments+ 0.1.0.17 ---------* Add `--no-socks5` flag to switch off the socks5 service on local+* Add `--disable-socks5` flag to switch off the socks5 service on local  0.1.0.16 --------
README.md view
@@ -1,7 +1,6 @@ MoeSocks ======== - A socks5 proxy using the client / server architecture.  MoeSocks is greatly inspired by [shadowsocks].@@ -11,40 +10,62 @@  type `moesocks --help` for help. +Usage+-----+* 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`.+ Features ---------* TCP port forwarding -* UDP port forwarding+* Socks5 proxy service, obviously+* TCP port forwarding+* UDP port forwarding, for example `-U 5300:8.8.8.8:53` * TCP per connection throttling (as a side effect of trying to find a bug in the remote)+* Socks5 service on local can be turned off+* Understand shadowsocks' configuration file  Not working ------------* Remote is flaky +* Remote is flaky, particularly when trying to send data through the Great+  Firewall of China (GFW). * UDP over Socks5 is not implemented-* 2.5 times slower then the original Python implementation (measured at 17.6M/s-    vs 43.6M/s on an Intel P8800, using the AES-256-CFB encryption)+* More then 2 times slower then the original Python implementation (measured at+  20M/s vs 43M/s on an Intel P8800, using the AES-256-CFB method)  Planning features ------------------ * None -Note+Notes ------ -There's a bug that prevents remote from working correctly.+There's a bug that prevents remote from working correctly with GFW.+Thanks, Prof Fang!  You should use the original Python implementation of [shadowsocks] on the remote-server.+server if your primary goal is to bypass internet censorship in China. -There is an earlier implementation of [shadowsocks in Haskell] by rnons that+From the author's limited testing, [shadowsocks-go] also works reasonably+well, though it might not run as fast as the Python's version.++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 the remote is too flaky. +shadowsocks, but it has been discarded since remote is too flaky.   [shadowsocks]:https://github.com/shadowsocks/shadowsocks -[shadowsocks in Haskell]:https://github.com/rnons/shadowsocks-haskell+[shadowsocks-go]:https://github.com/shadowsocks/shadowsocks-go+[shadowsocks-haskell]:https://github.com/rnons/shadowsocks-haskell   
config.json view
@@ -1,9 +1,9 @@ {-  "server":"localhost",-  "server_port":1190,-  "local_address":"localhost",-  "local_port":1090,+  "remote":"0.0.0.0",+  "remotePort":1190,+  "local":"localhost",+  "localPort":1090,   "password":"birthday!",-	"method":"aes-256-cfb"+  "method":"aes-256-cfb" } 
moesocks.cabal view
@@ -1,6 +1,6 @@ name:               moesocks category:           Network-version:            0.1.0.17+version:            0.1.0.18 license:            Apache-2.0 synopsis:           A functional firewall killer description:        A socks5 proxy using the client / server architecture.@@ -11,7 +11,7 @@ build-type:         Simple cabal-version:      >=1.10 copyright:          Copyright (C) 2015 Jinjing Wang-tested-with:        GHC == 7.10.1+tested-with:        GHC == 7.10.2  extra-source-files:     config.json                       , README.md@@ -26,27 +26,27 @@   ghc-options:        -Wall -fno-warn-unused-do-bind -threaded                        -rtsopts "-with-rtsopts=-N"   build-depends:       base > 4 && <= 5-                      , lens -                      , network+                      , HsOpenSSL+                      , aeson+                      , async+                      , attoparsec                       , binary                       , bytestring-                      , unix-                      , attoparsec-                      , text-                      , random-                      , optparse-applicative-                      , aeson-                      , unordered-containers-                      , HsOpenSSL                       , containers                       , cryptohash-                      , safe                       , hslogger-                      , transformers+                      , lens                        , mtl+                      , network+                      , optparse-applicative+                      , random+                      , safe                       , stm-                      , async+                      , text                       , time+                      , transformers+                      , unix+                      , unordered-containers    hs-source-dirs:      src   default-language:    Haskell2010
src/Network/MoeSocks/App.hs view
@@ -35,10 +35,35 @@ import qualified System.Log.Handler as LogHandler  -parseConfig :: Text -> MoeMonadT MoeConfig-parseConfig aFilePath = do-  _configFile <- io - TIO.readFile - aFilePath ^. _Text+parseConfig :: MoeOptions -> MoeMonadT MoeConfig+parseConfig aOption = do+  let+    formatConfig :: Value -> Value+    formatConfig (Object _obj) =+        Object -+          _obj+              & H.toList +              & over (mapped . _1) T.tail +              & H.fromList+    formatConfig _ = Null +    showConfig :: MoeConfig -> Text+    showConfig =    view utf8 +                  . toStrict +                  . encode +                  . formatConfig +                  . toJSON ++  let _maybeFilePath = aOption ^. configFile ++  _v <- case _maybeFilePath of+          Nothing -> pure - Just - Object mempty+          Just _filePath -> do+                              _data <- io - TIO.readFile - _filePath ^. _Text+                              pure -+                                (decodeStrict - review utf8 _data +                                    :: Maybe Value)+   let        fromShadowSocksConfig :: [(Text, Value)] -> [(Text, Value)]       fromShadowSocksConfig _configList = @@ -57,8 +82,7 @@       fromSS = fromShadowSocksConfig  -  let _v = decodeStrict - review utf8 _configFile :: Maybe Value-+  let        fixConfig :: Value -> Value       fixConfig (Object _obj) =           Object - @@ -72,25 +96,13 @@         -      formatConfig :: Value -> Value-      formatConfig (Object _obj) =-          Object --            _obj-                & H.toList -                & over (mapped . _1) T.tail -                & H.fromList-      formatConfig _ = Null        filterEssentialConfig :: Value -> Value       filterEssentialConfig (Object _obj) =           Object -             foldl (flip H.delete) _obj -                [-                "_remote"-              , "_remotePort"-              , "_local"-              , "_localPort"-              , "_password"+                "_password"               ]                  filterEssentialConfig _ = Null@@ -100,41 +112,42 @@           Object - _x `H.union` _y       mergeConfigObject _ _ = Null +      mergeParams :: [(Text, Value)] -> Value -> Value+      mergeParams xs (Object _x) =+          Object - H.fromList xs `H.union` _x+      mergeParams _ _ = Null +       optionalConfig = filterEssentialConfig - toJSON defaultMoeConfig       -      _maybeConfig = _v +      _maybeConfig = _v                       >>= decode                            . encode                            . flip mergeConfigObject optionalConfig+                          . mergeParams (aOption ^. params)                           . fixConfig  -  let -      showConfig :: MoeConfig -> ByteString-      showConfig = -                      toStrict -                    . encode -                    . formatConfig -                    . toJSON --   case _maybeConfig of     Nothing -> do       let _r =              execWriter - do               tell "\n\n"-              tell "Failed to parse configuration file\n"-              tell "Example: \n"--              let configBS :: ByteString  -                  configBS = showConfig defaultMoeConfig-              -              tell - configBS ^. utf8 <> "\n"+              case _maybeFilePath of+                Just _filePath -> do+                                    tell "Failed to parse configuration file"+                                    tell _filePath+                                    tell "\n"+                                    tell "Example: \n"+                                    tell - showConfig defaultMoeConfig <> "\n"+                Nothing -> do+                            tell "The password argument '-k' is required.\n"+                            tell "Alternatively, use '-c' to provide a "+                            tell "configuration file.\n"        throwError - _r ^. _Text       Just _config -> do-      let configStr = showConfig _config ^. utf8 . _Text :: String+      let configStr = showConfig _config ^. _Text :: String       io - puts - "Using config: " <> configStr       pure - _config                @@ -162,7 +175,7 @@      io - puts - show _options   -  _config <- parseConfig - _options ^. configFile+  _config <- parseConfig - _options   let _c = _config    let _method = _config ^. method . _Text@@ -173,8 +186,6 @@     case _cipher of       Nothing -> throwError - "Invalid method '"                                <> _method-                              <> "' in "-                              <> _options ^. configFile . _Text       Just _ -> pure ()    let localAppBuilder :: AppType -> String -> @@ -195,7 +206,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) -@@ -206,7 +217,7 @@             UDP_App -> do               let handleLocal = do                     (_msg, _sockAddr) <- -                        recvFrom _localSocket _ReceiveLength+                        recvFrom _localSocket _PacketLength                      puts - "L UDP: " <> show _msg                     @@ -263,7 +274,7 @@                 (_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) -@@ -280,7 +291,7 @@           bindSocket _remoteSocket _remoteAddr            let handleRemote = do-                (_msg, _sockAddr) <- recvFrom _remoteSocket _ReceiveLength+                (_msg, _sockAddr) <- recvFrom _remoteSocket _PacketLength                  puts - "R UDP: " <> show _msg 
src/Network/MoeSocks/Config.hs view
@@ -2,21 +2,20 @@  module Network.MoeSocks.Config where -import Network.MoeSocks.Constant import Network.MoeSocks.Type import System.Log.Logger  defaultMoeConfig :: MoeConfig defaultMoeConfig = MoeConfig   {-    _remote= "127.0.0.1"-  , _remotePort = 1190+    _remote= "0.0.0.0"+  , _remotePort = 8388    , _local = "127.0.0.1"-  , _localPort = 1090+  , _localPort = 1080   , _password = "moesocks"-  , _method = _DefaultMethod+  , _method = "aes-256-cfb"   , _timeout = 300-  , _tcpBufferSizeInPacket = 256+  , _tcpBufferSize = 256   , _throttle = False   , _throttleSpeed = 8000 -- in Kilobytes   }@@ -26,9 +25,10 @@ defaultMoeOptions = MoeOptions   {     _runningMode = DebugMode-  , _configFile = ""+  , _configFile = Nothing   , _verbosity = DEBUG   , _forwardTCP = []   , _forwardUDP = []   , _disableSocks5 = False+  , _params = []   } 
src/Network/MoeSocks/Constant.hs view
@@ -3,7 +3,6 @@ module Network.MoeSocks.Constant where  import Data.Word-import Data.Text (Text)  _No_authentication :: Word8 _No_authentication = 0@@ -17,11 +16,9 @@ _SpaceCode :: Word8 _SpaceCode = 32 --_DefaultMethod :: Text-_DefaultMethod = "aes-256-cfb"+_PacketLength :: Int+_PacketLength = 4096  -_ReceiveLength :: Int-_ReceiveLength = 4096-+_NoThrottle :: Maybe a+_NoThrottle = Nothing
src/Network/MoeSocks/Helper.hs view
@@ -79,8 +79,8 @@ showBytes :: ByteString -> String showBytes = show . S.unpack -sleep :: Int -> IO ()-sleep x = threadDelay - x * 1000 * 1000+sleep :: Double -> IO ()+sleep x = threadDelay - floor - x * 1000 * 1000  foreverRun :: IO a -> IO () foreverRun _io = do@@ -204,8 +204,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-}@@ -304,6 +304,7 @@       _produce :: Int -> IO ()       _produce _bytesReceived = do         _r <- timeoutFor aID aTimeout - recv_ aSocket+        {-pute - "Get chunk: " <> (show - S.length _r) -- <> " " <> aID-}         if (_r & isn't _Empty)            then do             forM_ aThrottle - \_throttle -> do@@ -348,13 +349,13 @@                     {-tryIO aID - close aSocket-}        _consume :: Int -> IO ()-      _consume _bytesSent = do+      _consume _allBytesSent = do         forM_ aThrottle - \_throttle -> do           _currentTime <- getCurrentTime           let _timeDiff = realToFrac (diffUTCTime _currentTime                                                    _startTime) :: Double -              _bytesK = fromIntegral _bytesSent / 1000 :: Double+              _bytesK = fromIntegral _allBytesSent / 1000 :: Double            let _speed = _bytesK / _timeDiff @@ -368,13 +369,18 @@                     <> " miliseconds."             threadDelay - floor - _sleepTime -        (atomically - readTBQueue aTBQueue) >>= \case +        +                            +        _newPacket <- atomically - readTBQueue aTBQueue++        case _newPacket of           Nothing -> () <$ _shutdown           Just _data -> do-                          timeoutFor aID aTimeout - sendAll aSocket _data+                          timeoutFor aID aTimeout - +                                          sendAll aSocket _data                           yield                           _consume - -                            _bytesSent + S.length _data+                            _allBytesSent + S.length _data      _consume 0 `onException` _shutdown   pure ()
src/Network/MoeSocks/Options.hs view
@@ -4,8 +4,12 @@  import Control.Lens import Data.Text.Lens+import Data.Aeson+import Data.Maybe+import Data.Text (Text) import Network.MoeSocks.Helper import Network.MoeSocks.Type+import Network.MoeSocks.Config import Options.Applicative hiding (Parser) import Prelude hiding ((-), takeWhile) import System.Log.Logger@@ -13,51 +17,114 @@ import Data.Attoparsec.Text (Parser, takeWhile, char, decimal, skipSpace,                                parseOnly, many', choice) +textOption :: O.Mod O.OptionFields String -> O.Parser Text+textOption x = strOption x <&> view (from _Text)++defaultHelp :: Text -> Text -> Mod f a+defaultHelp val x = help - x <> ", default: " <> val & view _Text++textParam :: O.Mod O.OptionFields String -> O.Parser (Maybe Value)+textParam = optional . fmap toJSON . textOption++intParam :: O.Mod O.OptionFields Int -> O.Parser (Maybe Value)+intParam = optional . fmap toJSON . option auto+ optionParser :: O.Parser MoeOptions optionParser = -  let _mode = ( strOption --                    short 'm'-                <>  long "mode"-                <>  metavar "MODE"-                <>  help "Tell moesocks to runs in local or remote mode"+  let _c = defaultMoeConfig+      _mode = ( textOption -+                    short 'r'+                <>  long "role"+                <>  metavar "ROLE"+                <>  defaultHelp "local" +                                "Tell moesocks to run as local or remote"               ) <|> pure "local"    -      parseMode :: String -> RunningMode+      parseMode :: Text -> RunningMode       parseMode x          | x `elem` ["server", "remote"] = RemoteMode         | x `elem` ["client", "local"] = LocalMode         | x == "debug" = DebugMode         | otherwise = DebugMode-  in -  let _disableSocks5 :: O.Parser Bool+      _disableSocks5 :: O.Parser Bool       _disableSocks5 = switch -                       long "disable-socks5"                   <>  help ("Do not start a socks5 server on local. It can be "                           <> "useful to run moesocks only as a secure tunnel")                   -  in-                            +      _tcpBufferSize = intParam -+                              long "tcp-buffer-size"+                          <>  metavar "SIZE"+                          <>  defaultHelp (_c ^. tcpBufferSize+                                            & show+                                            & view (from _Text))+                                          ("The number of packets used as a "+                                            <> "buffer. A packet can hold "+                                            <> "at most 4K of data.") -  let _config = ( strOption -+      _config = optional - textOption -                       short 'c'                   <>  long "config"                   <>  metavar "CONFIG"                   <>  help "Point to the path of the configuration file"-                ) <|> pure "config.json"                  -  in+      _remote = textParam -+                      short 's'+                  <>  metavar "REMOTE"+                  <>  defaultHelp (_c ^. remote)+                                  "remote address"+  +      _remotePort = intParam -+                          short 'p'+                      <>  metavar "REMOTE_PORT"+                      <>  defaultHelp (_c ^. remotePort +                                          & show +                                          & view (from _Text))+                                      "remote port" -  let _verbosity :: O.Parser Priority +      _local = textParam -+                      short 'b'+                  <>  metavar "LOCAL"+                  <>  defaultHelp (_c ^. local)+                                  "local address"++      _localPort = intParam -+                        short 'l'+                    <>  metavar "LOCAL PORT"+                    <>  defaultHelp (_c ^. localPort+                                        & show+                                        & view (from _Text))+                                    "local port"++      _password = textParam -+                        short 'k'+                    <>  metavar "PASSWORD"+                    <>  help "password"++      _method   = textParam -+                        short 'm'+                    <>  metavar "METHOD"+                    <> defaultHelp (_c ^. method)+                                    "encryption method"++      _timeout  = intParam - +                        short 't'+                    <>  metavar "TIMEOUT"+                    <>  defaultHelp (_c ^. timeout+                                        & show+                                        & view (from _Text))+                                    "timeout connection in seconds"+                        ++      _verbosity :: O.Parser Priority        _verbosity = flag INFO DEBUG -                           short 'v'                       <>  long "verbose"                       <>  help "Turn on logging"-  in --  let _forwardTCP :: O.Parser String-      _forwardTCP = ( strOption -+      _forwardTCP :: O.Parser (Maybe Text)+      _forwardTCP = optional - textOption -                           short 'T'                       <>  long "tcp"                       <>  metavar "port:host:hostport"@@ -67,13 +134,11 @@                                 <> "(client) host is to be forwarded to the "                                 <> "given host and port on the remote side."                                 )-                    ) <|> pure ""-                           -      _forwardUDP :: O.Parser String-      _forwardUDP = ( strOption -+      _forwardUDP :: O.Parser (Maybe Text)+      _forwardUDP = optional - textOption -                           short 'U'                       <>  long "udp"                       <>  metavar "port:host:hostport"@@ -83,8 +148,6 @@                                 <> "(client) host is to be forwarded to the "                                 <> "given host and port on the remote side."                                 )-                    ) <|> pure ""-          forwardParser ::  Parser Forward       forwardParser = do@@ -112,26 +175,46 @@       forwardListParser :: Parser [Forward]       forwardListParser = many' forwardParser -      parseForwarding :: String -> [Forward]-      parseForwarding x = -        x ^. from _Text +      parseForwarding :: Maybe Text -> [Forward]+      parseForwarding Nothing = []+      parseForwarding (Just x ) = +        x            & parseOnly forwardListParser            & toListOf (traverse . traverse)++      tag :: a -> O.Parser (Maybe b) -> O.Parser (Maybe (a, b))+      tag x = fmap . fmap - ((,) x)+++      _params :: O.Parser [(Text, Value)]+      _params = +        [ tag "_remote"         _remote    +        , tag "_remotePort"     _remotePort+        , tag "_local"          _local     +        , tag "_localPort"      _localPort +        , tag "_password"       _password+        , tag "_method"         _method +        , tag "_timeout"        _timeout+        , tag "_tcpBufferSize"  _tcpBufferSize+        ]+        & sequenceA+        & fmap catMaybes+             in             MoeOptions                <$> fmap parseMode _mode -              <*> fmap (view packed) _config+              <*> _config               <*> _verbosity               <*> fmap parseForwarding _forwardTCP               <*> fmap parseForwarding _forwardUDP               <*> _disableSocks5+              <*> _params  opts :: ParserInfo MoeOptions opts = info (helper <*> optionParser) -          fullDesc     <>  progDesc "A socks5 proxy using the client / server architecture"     <>  header "A functional firewall killer"- 
src/Network/MoeSocks/TCP.hs view
@@ -90,8 +90,8 @@           let                _header = shadowSocksRequestBuilder _clientRequest           -          sendChannel <- newTBQueueIO - aConfig ^. tcpBufferSizeInPacket-          receiveChannel <- newTBQueueIO - aConfig ^. tcpBufferSizeInPacket+          sendChannel <- newTBQueueIO - aConfig ^. tcpBufferSize+          receiveChannel <- newTBQueueIO - aConfig ^. tcpBufferSize            let _logId x = x <> " " <> _msg               _timeout = aConfig ^. timeout * 1000 * 1000@@ -112,7 +112,7 @@                 let _produce = do                                   produceLoop (_logId "L --> + Loop")                                     _timeout-                                    Nothing+                                    _NoThrottle                                     aSocket                                      sendChannel                                      _encrypt@@ -133,7 +133,7 @@           let receiveThread = do                 let _produce = produceLoop (_logId "L <-- + Loop")                                   _timeout-                                  Nothing+                                  _NoThrottle                                   __remoteSocket                                    receiveChannel                                   _decrypt@@ -141,7 +141,7 @@                 let _consume = do                                   consumeLoop (_logId "L <-- - Loop")                                     _timeout-                                    Nothing+                                    _NoThrottle                                     aSocket                                      receiveChannel                 finally @@ -190,8 +190,8 @@      let          handleTarget __leftOverBytes __targetSocket = do-          sendChannel <- newTBQueueIO - aConfig ^. tcpBufferSizeInPacket-          receiveChannel <- newTBQueueIO - aConfig ^. tcpBufferSizeInPacket+          sendChannel <- newTBQueueIO - aConfig ^. tcpBufferSize+          receiveChannel <- newTBQueueIO - aConfig ^. tcpBufferSize            let _logId x = x <> " " <> _msg               _timeout = aConfig ^. timeout * 1000 * 1000@@ -208,14 +208,14 @@                 let _produce = do                                   produceLoop (_logId "R --> + Loop")                                     _timeout-                                    Nothing+                                    _NoThrottle                                      aSocket                                     sendChannel                                     _decrypt                  let _consume = consumeLoop (_logId "R --> - Loop")                                   _timeout-                                  Nothing+                                  _NoThrottle                                   __targetSocket                                   sendChannel @@ -228,16 +228,16 @@            let receiveThread = do                 let _produce = do-                                  produceLoop (_logId "R --> + Loop")+                                  produceLoop (_logId "R <-- + Loop")                                     _timeout-                                    Nothing+                                    _NoThrottle                                      __targetSocket                                     receiveChannel                                     _encrypt                   let _consume = do-                                  consumeLoop (_logId "R --> - Loop")+                                  consumeLoop (_logId "R <-- - Loop")                                     _timeout                                     _throttle                                     aSocket
src/Network/MoeSocks/Type.hs view
@@ -55,7 +55,7 @@   , _password :: Text   , _method :: Text   , _timeout :: Int-  , _tcpBufferSizeInPacket :: Int+  , _tcpBufferSize :: Int -- in packets   , _throttle :: Bool   , _throttleSpeed :: Double   }@@ -86,11 +86,12 @@ data MoeOptions = MoeOptions   {     _runningMode :: RunningMode-  , _configFile :: Text+  , _configFile :: Maybe Text   , _verbosity :: Priority   , _forwardTCP :: [Forward]   , _forwardUDP :: [Forward]   , _disableSocks5 :: Bool+  , _params :: [(Text, Value)]   }   deriving (Show, Eq)