neko-obfs 0.1.0.1 → 0.1.0.2
raw patch · 5 files changed
+104/−48 lines, 5 files
Files
- Readme.md +6/−6
- neko-obfs.cabal +1/−1
- src/Main.hs +74/−27
- src/Options.hs +16/−9
- src/Type.hs +7/−5
Readme.md view
@@ -15,19 +15,19 @@ Implementation ============== -* `n` is randomly generated for each packet.-* `n` is bounded by a maximum `r`, configurable by the `--randomness` argument.-* To reduce overhead, `n` is set to 0 whenever `m` is greater then a - threshold `b`, configurable by the `--bound` argument.+* `n` is deduced from a randomly generated `i = n + m` for each packet.+* When a random `i` is less then `m`, payload is split to `p1` and `p2` to satisfy the constrain, where the length of `p1` is equal to `i` and `p1 + p2 = payload`. `p2` will be sent in the next iteration by the same algorithm.+* `i` is bounded by a maximum `r`, configurable by the `--randomnessInBytes` argument.+* To reduce overhead, `n` is set to `0` whenever `m` is greater then `r`. Usage ===== * local: - need-obfs --localHost TEXT --localPort INTEGER --remoteHost TEXT --remotePort INTEGER+ neko-obfs --localHost TEXT --localPort INTEGER --remoteHost TEXT --remotePort INTEGER * remote: - neko-obfs --remote --remoteHost TEXT --remotePort INTEGER --forwardHost TEXT --forwardPort INTEGER+ neko-obfs --role remote --remoteHost TEXT --remotePort INTEGER --forwardHost TEXT --forwardPort INTEGER * This tunnel should be used inside an encrypted tunnel. * For example:
neko-obfs.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: neko-obfs-version: 0.1.0.1+version: 0.1.0.2 synopsis: a TCP tunnel with packet length obfuscation description: Just another tool that helps accessing the internet license: Apache-2.0
src/Main.hs view
@@ -4,10 +4,11 @@ import Helper ((-), log) import Prelude hiding ((-), log)+import qualified Prelude as Prelude import Control.Concurrent.Async (async, waitAnyCatchCancel, waitEitherCancel) import Control.Lens-import Control.Monad (replicateM_, join)+import Control.Monad (replicateM_, join, when) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Trans.State.Strict (runStateT) import Data.Attoparsec.ByteString (parse, maybeResult)@@ -28,29 +29,69 @@ import Pipes.Safe import System.Random (randomIO) import Options.Generic+import System.IO (hSetBuffering, stdin, stdout, BufferMode(LineBuffering)) import Options (toConfig) import Parser import Type -obfsEncode :: (MonadIO m) => Integer -> Integer -> Pipe ByteString ByteString m a-obfsEncode bound randomness = do+obfsEncode :: (MonadIO m) => Bool -> Integer -> Pipe ByteString ByteString m a+obfsEncode debug randomness = do for cat - \x -> do let payloadLen = (fromIntegral - B.length x)+ if (payloadLen >= fromIntegral randomness)+ then do+ let paddingLen = 0+ let chunk =+ view strict - Builder.toLazyByteString -+ Builder.word32BE paddingLen+ <> Builder.byteString+ (BC.replicate (fromIntegral paddingLen) - '.')+ <> Builder.word32BE payloadLen+ <> Builder.byteString x - randomLen <-- if (payloadLen > fromIntegral bound)- then pure 0- else liftIO - randomIO <&> (`mod` fromIntegral randomness)+ when debug - log - "E: " <> (T.pack - show - B.length chunk)+ yield chunk+ else+ randomLenCheckLoop x+ where+ randomLenCheckLoop payload = do+ let payloadLen = (fromIntegral - B.length payload)+ randomLen <- liftIO - randomIO <&> (`mod` fromIntegral randomness)+ if (payloadLen <= randomLen)+ then+ do+ let paddingLen = randomLen Prelude.- payloadLen+ let chunk =+ view strict - Builder.toLazyByteString -+ Builder.word32BE paddingLen+ <> Builder.byteString+ (BC.replicate (fromIntegral paddingLen) - '.')+ <> Builder.word32BE payloadLen+ <> Builder.byteString payload - yield - view strict - Builder.toLazyByteString -- Builder.word32BE randomLen- <> Builder.byteString (BC.replicate (fromIntegral randomLen) - 'a')- <> Builder.word32BE payloadLen- <> Builder.byteString x+ when debug - log - "E: " <> (T.pack - show - B.length chunk)+ yield chunk+ else+ do+ let paddingLen = 0+ (now, next) = B.splitAt (fromIntegral randomLen) payload + let chunk =+ view strict - Builder.toLazyByteString -+ Builder.word32BE paddingLen+ <> Builder.byteString+ (BC.replicate (fromIntegral paddingLen) - '.')+ <> Builder.word32BE (fromIntegral - B.length now)+ <> Builder.byteString now + when debug - log - "E: " <> (T.pack - show - B.length chunk)+ yield chunk++ randomLenCheckLoop next++ obfsDecode :: (MonadIO m) => Producer ByteString m a -> (ByteString -> m ()) -> m () obfsDecode pull sink = loop pull where@@ -65,12 +106,15 @@ main :: IO () main = do+ hSetBuffering stdin LineBuffering+ hSetBuffering stdout LineBuffering+ config <- getRecord "a TCP tunnel with packet length obfuscation" <&> toConfig log - T.pack - show config - let mtu = 4096- timeout = fromIntegral - config ^. timeoutInSeconds * 1000000+ let _mtu = fromIntegral - config ^. mtu + (-8) -- 2 word32be+ _timeout = fromIntegral - config ^. timeoutInSeconds * 1000000 let _localHost = config ^. localHost . re packed _localPort = show - config ^. localPort@@ -78,9 +122,11 @@ _remoteHost = config ^. remoteHost . re packed _remotePort = show - config ^. remotePort - _fromSocket = fromSocketTimeout timeout- _toSocket = toSocketTimeout timeout+ _fromSocket = fromSocketTimeout _timeout+ _toSocket = toSocketTimeout _timeout + _debug = config ^. debug+ localThread <- pure - runSafeT . runEffect - serve (Host _localHost) _localPort - \(localSock, localSockAddr) -> do log - "local accepted: " <> view packed (show localSockAddr)@@ -91,14 +137,14 @@ liftIO - NS.setSocketOption remoteSock NS.NoDelay 1 let- localPull = _fromSocket localSock mtu+ localPull = _fromSocket localSock _mtu - remotePull = _fromSocket remoteSock mtu+ remotePull = _fromSocket remoteSock _mtu remotePush = _toSocket remoteSock sendThread <- liftIO - async - runEffect - do- localPull >-> obfsEncode (config ^. bound) (config ^. randomness)+ localPull >-> obfsEncode _debug (config ^. randomnessInBytes) >-> remotePush liftIO - NS.shutdown remoteSock NS.ShutdownSend @@ -123,17 +169,17 @@ liftIO - NS.setSocketOption forwardSock NS.NoDelay 1 let- remotePull = _fromSocket remoteSock mtu+ remotePull = _fromSocket remoteSock _mtu remotePush = _toSocket remoteSock - forwardPull = _fromSocket forwardSock mtu+ forwardPull = _fromSocket forwardSock _mtu sendThread <- liftIO - async - do obfsDecode remotePull - send forwardSock liftIO - NS.shutdown forwardSock NS.ShutdownSend recvThread <- liftIO - async - runEffect - do- forwardPull >-> obfsEncode (config ^. bound) (config ^. randomness)+ forwardPull >-> obfsEncode _debug (config ^. randomnessInBytes) >-> remotePush liftIO - NS.shutdown remoteSock NS.ShutdownSend @@ -143,11 +189,12 @@ case config ^. role of- Local -> do- _ <- localThread- pure ()- Remote -> do- _ <- remoteThread+ Local -> join localThread+ Remote -> join remoteThread+ Both -> do+ local <- async - join localThread+ remote <- async - join remoteThread+ waitEitherCancel local remote pure () pure ()
src/Options.hs view
@@ -1,6 +1,5 @@--- |- {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-} module Options where @@ -11,16 +10,17 @@ data TunnelOptions = TunnelOptions {- localHost :: Maybe Text+ role :: Maybe Text+ , localHost :: Maybe Text , localPort :: Maybe Integer , remoteHost :: Text , remotePort :: Integer , forwardHost :: Maybe Text , forwardPort :: Maybe Integer- , remote :: Bool- , bound :: Maybe Integer- , randomness :: Maybe Integer+ , randomnessInBytes :: Maybe Integer , timeoutInSeconds :: Maybe Integer+ , mtu :: Maybe Integer+ , debug :: Bool } deriving (Generic, Show, Eq) @@ -30,6 +30,12 @@ toConfig :: TunnelOptions -> Config toConfig o = let c = defaultConfig+ _role =+ case role o of+ Just "remote" -> Remote+ Just "local" -> Local+ Just "both" -> Both+ _ -> Local in Config {@@ -39,8 +45,9 @@ , _remotePort = remotePort o , _forwardHost = fromMaybe (_forwardHost c) (forwardHost o) , _forwardPort = fromMaybe (_forwardPort c) (forwardPort o)- , _role = if remote o then Remote else Local- , _bound = fromMaybe (_bound c) (bound o)- , _randomness = fromMaybe (_randomness c) (randomness o)+ , _role = _role+ , _randomnessInBytes = fromMaybe (_randomnessInBytes c) (randomnessInBytes o) , _timeoutInSeconds = fromMaybe (_timeoutInSeconds c) (timeoutInSeconds o)+ , _mtu = fromMaybe (_mtu c) (mtu o)+ , _debug = debug o }
src/Type.hs view
@@ -7,7 +7,7 @@ import Control.Lens import Data.Text (Text) -data Role = Local | Remote+data Role = Local | Remote | Both deriving (Show, Eq) data Config = Config@@ -19,9 +19,10 @@ , _forwardHost :: Text , _forwardPort :: Integer , _role :: Role- , _bound :: Integer- , _randomness :: Integer+ , _randomnessInBytes :: Integer , _timeoutInSeconds :: Integer+ , _debug :: Bool+ , _mtu :: Integer } deriving (Show, Eq) @@ -37,7 +38,8 @@ , _forwardHost = "127.0.0.1" , _forwardPort = 1080 , _role = Local- , _bound = 1024- , _randomness = 300+ , _randomnessInBytes = 256 , _timeoutInSeconds = 3600+ , _debug = False+ , _mtu = 4096 }