moesocks-0.1.2.10: src/Network/MoeSocks/App.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
module Network.MoeSocks.App where
import Control.Concurrent
import Control.Concurrent.STM
import Control.Concurrent.Async hiding (waitBoth)
import Control.Lens
import Control.Monad
import Control.Monad.Except
import Control.Monad.Reader hiding (local)
import Control.Monad.Writer hiding (listen)
import Data.ByteString (ByteString)
import Data.Text.Lens
import Network.MoeSocks.Common
import Network.MoeSocks.Encrypt (initCipherBox, safeMethods, unsafeMethods)
import Network.MoeSocks.Helper
import Network.MoeSocks.Modules.Resource (loadConfig)
import Network.MoeSocks.Runtime
import Network.MoeSocks.TCP
import Network.MoeSocks.Type
import Network.MoeSocks.UDP
import Network.Socket hiding (send, recv, recvFrom, sendTo)
import Network.Socket.ByteString
import Prelude hiding ((-), take)
_PacketLength :: Int
_PacketLength = 4096
withGateOptions :: Options -> IO a -> IO ()
withGateOptions aOption aIO = do
if aOption ^. listMethods
then do
let _br = putStrLn ""
_br
putStrLn "Recommended:"
itraverse_ (\k _ -> putStrLn - "\t\t" <> k ^. _Text) - safeMethods
_br
putStrLn "Supported:"
itraverse_ (\k _ -> putStrLn - "\t\t" <> k ^. _Text) - unsafeMethods
else
() <$ aIO
data ServiceType = TCP_Service | UDP_Service
deriving (Show, Eq)
moeApp:: MoeMonadT ()
moeApp = do
_options <- ask
io - initLogger - _options ^. verbosity
io - debug_ - show _options
_config <- loadConfig - _options
let _c = _config
let _method = _config ^. method
_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 localService :: ServiceType
-> String
-> (ByteString -> (Socket, SockAddr) -> IO ())
-> (Socket, SockAddr)
-> IO ()
localService aServiceType aID aHandler s =
logSA "L loop" (pure s) - \(_localSocket, _localAddr) -> do
setSocketOption _localSocket ReuseAddr 1
bindSocket _localSocket _localAddr
case aServiceType of
TCP_Service -> do
info_ - "LT: " <> aID <> " nyaa!"
when (_c ^. fastOpen) -
setSocket_TCP_FAST_OPEN _localSocket
listen _localSocket maxListenQueue
let handleLocal _socket = do
_s@(_newSocket, _newSockAddr) <- accept _socket
setSocketCloseOnExec _newSocket
setSocketConfig _c _socket
forkIO - catchExceptAsyncLog "LT" -
logSA "L TCP client socket" (pure _s) -
aHandler ""
forever - handleLocal _localSocket
UDP_Service -> do
info_ - "LU: " <> aID <> " nyaa!"
let handleLocal = do
(_msg, _sockAddr) <-
recvFrom _localSocket _PacketLength
debug_ - "L UDP: " <> show _msg
let _s = (_localSocket, _sockAddr)
forkIO - catchExceptAsyncLog "LU" -
aHandler _msg _s
forever handleLocal
let
showWrapped :: (Show a) => a -> String
showWrapped x = "[" <> show x <> "]"
let local_SOCKS5 :: (Socket, SockAddr) -> IO ()
local_SOCKS5 _s = localService TCP_Service
("SOCKS5 proxy " <> showWrapped (_s ^. _2))
(local_SOCKS5_RequestHandler _env) - _s
showForwarding :: Forward -> String
showForwarding (Forward _localPort _remoteHost _remotePort) =
"["
<> show _localPort
<> " -> "
<> _remoteHost ^. _Text
<> ":"
<> show _remotePort
<> "]"
localForward_TCP :: Forward -> (Socket, SockAddr) -> IO ()
localForward_TCP _f _s = do
let _m = showForwarding _f
localService TCP_Service ("TCP port forwarding " <> _m)
(local_TCP_ForwardRequestHandler _env _f)
_s
localForward_UDP :: Forward -> (Socket, SockAddr) -> IO ()
localForward_UDP _f _s = do
let _m = showForwarding _f
localService UDP_Service ("UDP port forwarding " <> _m)
(local_UDP_ForwardRequestHandler _env _f)
_s
let remote_TCP_Relay :: (Socket, SockAddr) -> IO ()
remote_TCP_Relay s = logSA "R loop" (pure s) -
\(_remoteSocket, _remoteAddr) -> do
info_ - "RT: TCP relay " <> showWrapped _remoteAddr <> " nyaa!"
setSocketOption _remoteSocket ReuseAddr 1
bindSocket _remoteSocket _remoteAddr
when (_c ^. fastOpen) -
setSocket_TCP_FAST_OPEN _remoteSocket
listen _remoteSocket maxListenQueue
let handleRemote _socket = do
(_newSocket, _) <- accept _socket
setSocketCloseOnExec _newSocket
setSocketConfig _c _socket
forkIO - catchExceptAsyncLog "RT" -
logSocket "R remote socket" (pure _newSocket) -
remote_TCP_RequestHandler _env
forever - handleRemote _remoteSocket
let remote_UDP_Relay :: (Socket, SockAddr) -> IO ()
remote_UDP_Relay s = logSA "R loop" (pure s) -
\(_remoteSocket, _remoteAddr) -> do
info_ - "RU: UDP relay " <> showWrapped _remoteAddr <> " nyaa!"
setSocketOption _remoteSocket ReuseAddr 1
bindSocket _remoteSocket _remoteAddr
let handleRemote = do
(_msg, _sockAddr) <- recvFrom _remoteSocket _PacketLength
debug_ - "R UDP: " <> show _msg
let _s = (_remoteSocket, _sockAddr)
forkIO - catchExceptAsyncLog "RU" -
remote_UDP_RequestHandler _env _msg _s
forever handleRemote
-- Run
let runRemoteRelay :: RemoteRelay -> IO ()
runRemoteRelay _remoteRelay = do
let _address = _remoteRelay ^. remoteRelayAddress
_port = _remoteRelay ^. remoteRelayPort
case _remoteRelay ^. remoteRelayType of
Remote_TCP_Relay -> catchExceptAsyncLog "R TCP Relay" - do
getSocket _address _port Stream
>>= remote_TCP_Relay
Remote_UDP_Relay -> catchExceptAsyncLog "R UDP Relay" - do
getSocket _address _port Datagram
>>= remote_UDP_Relay
let runLocalService :: LocalService -> IO ()
runLocalService _localService = do
case _localService ^. localServiceType of
LocalService_TCP_Forward forwarding ->
catchExceptAsyncLog "L TCP_Forwarding" - do
getSocket (_localService ^. localServiceAddress)
(forwarding ^. forwardLocalPort)
Stream
>>= localForward_TCP forwarding
LocalService_UDP_Forward forwarding ->
catchExceptAsyncLog "L UDP_Forwarding" - do
getSocket (_localService ^. localServiceAddress)
(forwarding ^. forwardLocalPort)
Datagram
>>= localForward_UDP forwarding
LocalService_SOCKS5 _port ->
catchExceptAsyncLog "L SOCKS5" - do
getSocket (_localService ^. localServiceAddress) _port Stream
>>= local_SOCKS5
runJob :: Job -> TVar JobStatus -> IO ()
runJob (RemoteRelayJob x) _ = runRemoteRelay x
runJob (LocalServiceJob x) _ = runLocalService x
runApp :: [Job] -> IO ()
runApp someJobs = do
_jobs <- (forM someJobs - \_job -> (,) _job
<$> newTVarIO initialJobStatus)
_asyncs <- mapM (async . foreverRun . (uncurry runJob)) _jobs
_mainThread <- async - do
waitAnyCancel - _asyncs
_uiThread <- async - forever - do
let _statuses = _jobs ^.. each . _2 :: [TVar JobStatus]
{-forM_ _statuses - readTVarIO >=> print-}
sleep 5
waitAnyCancel [_mainThread, _uiThread]
pure ()
io - runApp - filterJobs (_options ^. runningMode) - loadJobs _c _options