-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathServer.hs
120 lines (100 loc) · 3.79 KB
/
Server.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
{-# LANGUAGE RecordWildCards, ScopedTypeVariables #-}
module Main where
import Control.Applicative
import Control.Monad
import Control.Concurrent
import Control.Monad.Reader
import Control.Exception
import qualified Data.ByteString as B
import System.Environment
import System.IO
import System.Info
import System.Exit
import System.Process
import Network.Socket
import Network.BSD
import Dispatch
import Protocol
import Message
import Compat
wLog lock s = do
withMVar lock $ \ () -> hPutStrLn stderr $ "[Server] " ++ s
main = do
args <- getArgs
case args of
[user, host, remotePath, fwdType, fwdArg] -> do
logLock <- newMVar ()
let fwdOpt = parseFwdOpt fwdType fwdArg
writeLog = wLog logLock
writeLog (show fwdOpt)
waitForCallable $ \ callInMain -> forkIO $ do
(rMsg, wMsg, sshErr) <- mkSshTransport user host 22
remotePath callInMain
chanMan <- mkChanManager
forkIO $ forever $ do
debugMsg <- hGetLine sshErr
writeLog debugMsg
let protoState = ProtocolState fwdOpt rMsg wMsg writeLog chanMan
runProtocol protoState runLocalPart
_ -> hPutStr stderr usage
where
usage = unlines [ "Usage: Server USER HOST REMOTE-PATH FWD-SPEC"
, "where FWD-SPEC can be:"
, " -D LOCALPORT"
, " -DR REMOTEPORT"
, " -L LOCALPORT:REMOTEHOST:REMOTEPORT"
, " -R REMOTEPORT:LOCALHOST:LOCALPORT"
]
parseFwdOpt :: String -> String -> FwdOption
parseFwdOpt fwdType = case fwdType of
"-D" -> FwdLocalDynamic . mkPortNum . read
"-DR" -> FwdRemoteDynamic . mkPortNum . read
"-L" -> splitArgWith FwdLocal
"-R" -> splitArgWith FwdRemote
where
splitArgWith constr fwdArg = let (p, h, p') = splitFwdArg fwdArg
in constr (mkPortNum p) h (mkPortNum p')
splitFwdArg fwdArg = (read port1, host, read port2)
where
[port1, host, port2] = split ':' fwdArg
mkPortNum :: Int -> PortNumber
mkPortNum = fromIntegral
mkSshTransport :: String -> String -> Int -> String -> (IO () -> IO ()) ->
IO (ReadMsg, WriteMsg, Handle)
mkSshTransport user host port command callInMain = do
(Just sshIn, Just sshOut, Just sshErr, _) <- createProcess $
sshProc { std_in = CreatePipe, std_out = CreatePipe
, std_err = CreatePipe }
forM_ [sshIn, sshOut, sshErr] $ \ h -> do
hSetBuffering h NoBuffering
hSetBinaryMode h True
rChan <- newChan
wChan <- newChan
let
handleErr (e :: SomeException) = do
callInMain $ exitSuccess
forkIO $ (`catchEx` handleErr) $ dispatchToChan sshOut rChan
forkIO $ (`catchEx` handleErr) $ dispatchFromChan wChan sshIn
-- SSH connection established.
return (readChan rChan, writeChan wChan, sshErr)
where
sshProc
| os == "linux" = proc "/usr/bin/ssh" [ user ++ "@" ++ host
, "-p", show port
, command
]
| os == "mingw32" = proc "./plink.exe" [ "-l", user, host
, "-i", "./putty-priv-key"
, "-P", show port
, command
]
| otherwise = error $ "sshProc: unsupported os: " ++ os
runLocalPart = do
writeMsg <- asks protoWriteMsg
fwdOpt <- asks protoFwdOpt
liftIO $ writeMsg (HandShake fwdOpt)
case fwdOpt of
FwdLocal {..} -> runLocalServer handleLocalFwdReq
FwdLocalDynamic {..} -> runLocalServer handleSocks5ClientReq
FwdRemote {..} -> runPortForwarder
FwdRemoteDynamic {..} -> runPortForwarder