summaryrefslogtreecommitdiff
path: root/Remote
diff options
context:
space:
mode:
authorGravatar Joey Hess <joeyh@joeyh.name>2016-11-21 19:24:55 -0400
committerGravatar Joey Hess <joeyh@joeyh.name>2016-11-21 19:24:55 -0400
commit1100263e70da68607882e7a63d945e0a1f1fea90 (patch)
tree6f845352f3c3dd2fc155685d77ca952a5a9827db /Remote
parent01bf227ad1d9bd30d6fad2dc104b264a1f55c2c4 (diff)
pull/push over tor working now
Still a couple bugs: * Closing the connection to the server leaves git upload-pack / receive-pack running, which could be used to DOS. * Sometimes the data is transferred, but it fails at the end, sometimes with: git-remote-tor-annex: <socket: 10>: commitBuffer: resource vanished (Broken pipe) Must be a race condition around shutdown.
Diffstat (limited to 'Remote')
-rw-r--r--Remote/Helper/P2P.hs70
-rw-r--r--Remote/Helper/P2P/IO.hs152
2 files changed, 109 insertions, 113 deletions
diff --git a/Remote/Helper/P2P.hs b/Remote/Helper/P2P.hs
index 7e49968ee..0e604a50d 100644
--- a/Remote/Helper/P2P.hs
+++ b/Remote/Helper/P2P.hs
@@ -141,30 +141,18 @@ data NetF c
| SendBytes Len L.ByteString c
| ReceiveBytes Len (L.ByteString -> c)
| CheckAuthToken UUID AuthToken (Bool -> c)
- | Relay RelayHandle
- (RelayData -> Net (Maybe ExitCode))
- (ExitCode -> c)
- -- ^ Waits for data to be written to the RelayHandle, and for messages
- -- to be received from the peer, and passes the data to the
- -- callback, continuing until it returns an ExitCode.
- | RelayService Service
- (RelayHandle -> RelayData -> Net (Maybe ExitCode))
- (ExitCode -> c)
- -- ^ Runs a service, and waits for it to output to stdout,
- -- and for messages to be received from the peer, and passes
- -- the data to the callback (which is also passed the service's
- -- stdin RelayHandle), continuing uniil the service exits.
- | WriteRelay RelayHandle L.ByteString c
- -- ^ Write data to a relay's handle, flushing it immediately.
+ | RelayService Service c
+ -- ^ Runs a service, relays its output to the peer, and data
+ -- from the peer to it.
+ | Relay RelayHandle RelayHandle (ExitCode -> c)
+ -- ^ Reads from the first RelayHandle, and sends the data to a
+ -- peer, while at the same time accepting input from the peer
+ -- which is sent the the second RelayHandle. Continues until
+ -- the peer sends an ExitCode.
deriving (Functor)
type Net = Free NetF
-data RelayData
- = RelayData L.ByteString
- | RelayMessage Message
- deriving (Show)
-
newtype RelayHandle = RelayHandle Handle
data LocalF c
@@ -212,8 +200,7 @@ runNet (SendBytes _ _ next) ms = ("> bytes", Nothing):runPure next ms
runNet (ReceiveBytes _ next) ms = ("< bytes", Nothing):runPure (next L.empty) ms
runNet (CheckAuthToken _ _ next) ms = runPure (next True) ms
runNet (Relay _ _ next) ms = runPure (next ExitSuccess) ms
-runNet (RelayService _ _ next) ms = runPure (next ExitSuccess) ms
-runNet (WriteRelay _ _ next) ms = runPure next ms
+runNet (RelayService _ next) ms = runPure next ms
runLocal :: Show r => LocalF (Proto r) -> [Message] -> [(String, Maybe Message)]
runLocal (KeyFileSize _ next) ms = runPure (next (Len 100)) ms
@@ -341,9 +328,7 @@ serve myuuid = go Nothing
-- setPresent not called because the peer may have
-- requested the data but not permanatly stored it.
GET offset key -> void $ sendContent key offset
- CONNECT service -> do
- exitcode <- net $ relayService service relayCallback
- net $ sendMessage (CONNECTDONE exitcode)
+ CONNECT service -> net $ relayService service
_ -> net $ sendMessage (ERROR "unexpected command")
sendContent :: Key -> Offset -> Proto Bool
@@ -399,19 +384,28 @@ readKeyFileLen key (Offset offset) = do
connect :: Service -> Handle -> Handle -> Proto ExitCode
connect service hin hout = do
net $ sendMessage (CONNECT service)
- net $ relay (RelayHandle hin) (relayCallback (RelayHandle hout))
-
-relayCallback :: RelayHandle -> RelayData -> Net (Maybe ExitCode)
-relayCallback hout (RelayMessage (DATA len)) = do
- writeRelay hout =<< receiveBytes len
- return Nothing
-relayCallback _ (RelayMessage (CONNECTDONE exitcode)) =
- return (Just exitcode)
-relayCallback _ (RelayMessage m) = do
- sendMessage $ ERROR $ "expected DATA or CONNECTDONE not " ++ unwords (Proto.formatMessage m)
- return (Just (ExitFailure 1))
-relayCallback _ (RelayData b) = do
+ net $ relay (RelayHandle hin) (RelayHandle hout)
+
+data RelayData
+ = RelayToPeer L.ByteString
+ | RelayFromPeer L.ByteString
+ | RelayDone ExitCode
+ deriving (Show)
+
+relayFromPeer :: Net RelayData
+relayFromPeer = do
+ r <- receiveMessage
+ case r of
+ CONNECTDONE exitcode -> return $ RelayDone exitcode
+ DATA len -> RelayFromPeer <$> receiveBytes len
+ _ -> do
+ sendMessage $ ERROR "expected DATA or CONNECTDONE"
+ return $ RelayDone $ ExitFailure 1
+
+relayToPeer :: RelayData -> Net ()
+relayToPeer (RelayDone exitcode) = sendMessage (CONNECTDONE exitcode)
+relayToPeer (RelayToPeer b) = do
let len = Len $ fromIntegral $ L.length b
sendMessage (DATA len)
sendBytes len b
- return Nothing
+relayToPeer (RelayFromPeer _) = return ()
diff --git a/Remote/Helper/P2P/IO.hs b/Remote/Helper/P2P/IO.hs
index c6a80cdbf..b3597abf9 100644
--- a/Remote/Helper/P2P/IO.hs
+++ b/Remote/Helper/P2P/IO.hs
@@ -14,7 +14,6 @@ module Remote.Helper.P2P.IO
import Remote.Helper.P2P
import Utility.Process
-import Types.UUID
import Git
import Git.Command
import Utility.SafeCommand
@@ -24,10 +23,10 @@ import Utility.Exception
import Control.Monad
import Control.Monad.Free
import Control.Monad.IO.Class
-import Data.Maybe
import System.Exit (ExitCode(..))
import System.IO
import Control.Concurrent
+import Control.Concurrent.Async
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
@@ -58,7 +57,6 @@ runNetHandle s runner f = case f of
runner next
ReceiveMessage next -> do
l <- liftIO $ hGetLine (ihdl s)
- -- liftIO $ hPutStrLn stderr ("< " ++ show l)
case parseMessage l of
Just m -> runner (next m)
Nothing -> runner $ do
@@ -72,64 +70,43 @@ runNetHandle s runner f = case f of
runner next
ReceiveBytes (Len n) next -> do
b <- liftIO $ L.hGet (ihdl s) (fromIntegral n)
- --liftIO $ hPutStrLn stderr $ "!!!" ++ show (L.length b)
runner (next b)
CheckAuthToken u t next -> do
authed <- return True -- TODO XXX FIXME really check
runner (next authed)
- Relay hout callback next ->
- runRelay runner hout callback >>= runner . next
- RelayService service callback next ->
- runRelayService s runner service callback >>= runner . next
- WriteRelay (RelayHandle h) b next -> do
- liftIO $ do
- -- L.hPut h b
- hPutStrLn h (show ("relay got:", b, L.length b))
- hFlush h
- runner next
+ Relay hin hout next ->
+ runRelay runner hin hout >>= runner . next
+ RelayService service next ->
+ runRelayService s runner service >> runner next
runRelay
:: MonadIO m
=> RunProto
-> RelayHandle
- -> (RelayData -> Net (Maybe ExitCode))
+ -> RelayHandle
-> m ExitCode
-runRelay runner (RelayHandle hout) callback = do
- v <- liftIO newEmptyMVar
- _ <- liftIO $ forkIO $ readout v
- feeder <- liftIO $ forkIO $ feedin v
- exitcode <- liftIO $ drain v
- liftIO $ killThread feeder
- return exitcode
+runRelay runner (RelayHandle hout) (RelayHandle hin) = liftIO $
+ bracket setup cleanup go
where
- feedin v = forever $ do
- m <- runner $ net receiveMessage
- putMVar v $ RelayMessage m
+ setup = do
+ v <- newEmptyMVar
+ void $ forkIO $ relayFeeder runner v
+ void $ forkIO $ relayReader v hout
+ return v
- readout v = do
- b <- B.hGetSome hout 65536
- if B.null b
- then hClose hout
- else do
- putMVar v $ RelayData (L.fromChunks [b])
- readout v
-
- drain v = do
- d <- takeMVar v
- liftIO $ hPutStrLn stderr (show d)
- r <- runner $ net $ callback d
- case r of
- Nothing -> drain v
- Just exitcode -> return exitcode
+ cleanup _ = do
+ hClose hin
+ hClose hout
+
+ go v = relayHelper runner v hin
runRelayService
- :: (MonadIO m, MonadMask m)
+ :: MonadIO m
=> S
-> RunProto
-> Service
- -> (RelayHandle -> RelayData -> Net (Maybe ExitCode))
- -> m ExitCode
-runRelayService s runner service callback = bracket setup cleanup go
+ -> m ()
+runRelayService s runner service = liftIO $ bracket setup cleanup go
where
cmd = case service of
UploadPack -> "upload-pack"
@@ -141,45 +118,70 @@ runRelayService s runner service callback = bracket setup cleanup go
] (repo s)
setup = do
- v <- liftIO newEmptyMVar
- (Just hin, Just hout, _, pid) <- liftIO $
- createProcess serviceproc
- { std_out = CreatePipe
- , std_in = CreatePipe
- }
- feeder <- liftIO $ forkIO $ feedin v
- return (v, feeder, hin, hout, pid)
-
- cleanup (_, feeder, hin, hout, pid) = liftIO $ do
+ (Just hin, Just hout, _, pid) <- createProcess serviceproc
+ { std_out = CreatePipe
+ , std_in = CreatePipe
+ }
+ v <- newEmptyMVar
+ feeder <- async $ relayFeeder runner v
+ reader <- async $ relayReader v hout
+ waiter <- async $ waitexit v pid
+ return (v, feeder, reader, waiter, hin, hout, pid)
+
+ cleanup (_, feeder, reader, waiter, hin, hout, pid) = do
+ hPutStrLn stderr "!!!!\n\nIN CLEANUP"
+ hFlush stderr
hClose hin
hClose hout
- liftIO $ killThread feeder
+ cancel reader
+ cancel waiter
void $ waitForProcess pid
- go (v, _, hin, hout, pid) = do
- _ <- liftIO $ forkIO $ readout v hout
- _ <- liftIO $ forkIO $ putMVar v . Left =<< waitForProcess pid
- liftIO $ drain v hin
+ go (v, _, _, _, hin, _, _) = do
+ exitcode <- relayHelper runner v hin
+ runner $ net $ relayToPeer (RelayDone exitcode)
+
+ waitexit v pid = putMVar v . RelayDone =<< waitForProcess pid
- drain v hin = do
+-- Processes RelayData as it is put into the MVar.
+relayHelper :: RunProto -> MVar RelayData -> Handle -> IO ExitCode
+relayHelper runner v hin = loop
+ where
+ loop = do
d <- takeMVar v
case d of
- Left exitcode -> return exitcode
- Right relaydata -> do
- liftIO $ hPutStrLn stderr ("> " ++ show relaydata)
- _ <- runner $ net $
- callback (RelayHandle hin) relaydata
- drain v hin
-
- readout v hout = do
+ RelayFromPeer b -> do
+ L.hPut hin b
+ hFlush hin
+ loop
+ RelayToPeer b -> do
+ runner $ net $ relayToPeer (RelayToPeer b)
+ loop
+ RelayDone exitcode -> do
+ runner $ net $ relayToPeer (RelayDone exitcode)
+ return exitcode
+
+-- Takes input from the peer, and puts it into the MVar for processing.
+-- Repeats until the peer tells it it's done.
+relayFeeder :: RunProto -> MVar RelayData -> IO ()
+relayFeeder runner v = loop
+ where
+ loop = do
+ rd <- runner $ net relayFromPeer
+ putMVar v rd
+ case rd of
+ RelayDone _ -> return ()
+ _ -> loop
+
+-- Reads input from the Handle and puts it into the MVar for relaying to
+-- the peer. Continues until EOF on the Handle.
+relayReader :: MVar RelayData -> Handle -> IO ()
+relayReader v hout = loop
+ where
+ loop = do
b <- B.hGetSome hout 65536
if B.null b
then return ()
else do
- putMVar v $ Right $
- RelayData (L.fromChunks [b])
- readout v hout
-
- feedin v = forever $ do
- m <- runner $ net receiveMessage
- putMVar v $ Right $ RelayMessage m
+ putMVar v $ RelayToPeer (L.fromChunks [b])
+ loop