2016-12-09 20:02:43 +00:00
|
|
|
{- git-remote-daemon, tor hidden service server and transport
|
2016-11-20 19:45:01 +00:00
|
|
|
-
|
|
|
|
- Copyright 2016 Joey Hess <id@joeyh.name>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
2016-12-09 20:02:43 +00:00
|
|
|
module RemoteDaemon.Transport.Tor (server, transport) where
|
2016-11-20 19:45:01 +00:00
|
|
|
|
|
|
|
import Common
|
2016-12-02 17:50:56 +00:00
|
|
|
import qualified Annex
|
|
|
|
import Annex.Concurrent
|
2016-12-09 19:08:54 +00:00
|
|
|
import Annex.ChangedRefs
|
2016-11-20 19:45:01 +00:00
|
|
|
import RemoteDaemon.Types
|
|
|
|
import RemoteDaemon.Common
|
|
|
|
import Utility.Tor
|
|
|
|
import Utility.FileMode
|
2016-11-30 20:38:16 +00:00
|
|
|
import Utility.AuthToken
|
2016-12-09 20:02:43 +00:00
|
|
|
import P2P.Protocol as P2P
|
2016-12-02 19:34:15 +00:00
|
|
|
import P2P.IO
|
2016-12-02 17:50:56 +00:00
|
|
|
import P2P.Annex
|
2016-11-30 20:38:16 +00:00
|
|
|
import P2P.Auth
|
2016-12-09 20:02:43 +00:00
|
|
|
import P2P.Address
|
2016-11-20 19:45:01 +00:00
|
|
|
import Annex.UUID
|
|
|
|
import Types.UUID
|
2016-11-22 02:03:29 +00:00
|
|
|
import Messages
|
|
|
|
import Git
|
2016-12-09 20:02:43 +00:00
|
|
|
import Git.Command
|
2016-11-20 19:45:01 +00:00
|
|
|
|
|
|
|
import System.PosixCompat.User
|
|
|
|
import Control.Concurrent
|
|
|
|
import System.Log.Logger (debugM)
|
2016-11-22 02:03:29 +00:00
|
|
|
import Control.Concurrent.STM
|
2016-12-09 20:02:43 +00:00
|
|
|
import Control.Concurrent.Async
|
|
|
|
import qualified Network.Socket as S
|
2016-11-20 19:45:01 +00:00
|
|
|
|
|
|
|
-- Run tor hidden service.
|
|
|
|
server :: TransportHandle -> IO ()
|
|
|
|
server th@(TransportHandle (LocalRepo r) _) = do
|
|
|
|
u <- liftAnnex th getUUID
|
2016-11-22 02:03:29 +00:00
|
|
|
|
|
|
|
q <- newTBQueueIO maxConnections
|
|
|
|
replicateM_ maxConnections $
|
2016-11-30 20:38:16 +00:00
|
|
|
forkIO $ forever $ serveClient th u r q
|
2016-11-22 02:03:29 +00:00
|
|
|
|
2016-11-20 19:45:01 +00:00
|
|
|
uid <- getRealUserID
|
|
|
|
let ident = fromUUID u
|
2016-11-29 19:43:34 +00:00
|
|
|
let sock = hiddenServiceSocketFile uid ident
|
2016-11-20 19:45:01 +00:00
|
|
|
nukeFile sock
|
2016-12-09 20:02:43 +00:00
|
|
|
soc <- S.socket S.AF_UNIX S.Stream S.defaultProtocol
|
|
|
|
S.bind soc (S.SockAddrUnix sock)
|
2016-11-20 19:45:01 +00:00
|
|
|
-- Allow everyone to read and write to the socket; tor is probably
|
|
|
|
-- running as a different user. Connections have to authenticate
|
|
|
|
-- to do anything, so it's fine that other local users can connect.
|
|
|
|
modifyFileMode sock $ addModes
|
|
|
|
[groupReadMode, groupWriteMode, otherReadMode, otherWriteMode]
|
2016-12-09 20:02:43 +00:00
|
|
|
S.listen soc 2
|
2016-12-08 21:17:01 +00:00
|
|
|
debugM "remotedaemon" "Tor hidden service running"
|
2016-11-20 19:45:01 +00:00
|
|
|
forever $ do
|
2016-12-09 20:02:43 +00:00
|
|
|
(conn, _) <- S.accept soc
|
2016-12-06 19:40:31 +00:00
|
|
|
h <- setupHandle conn
|
2016-11-22 02:03:29 +00:00
|
|
|
ok <- atomically $ ifM (isFullTBQueue q)
|
|
|
|
( return False
|
|
|
|
, do
|
|
|
|
writeTBQueue q h
|
|
|
|
return True
|
|
|
|
)
|
|
|
|
unless ok $ do
|
2016-11-20 19:45:01 +00:00
|
|
|
hClose h
|
2016-12-08 21:17:01 +00:00
|
|
|
warningIO "dropped Tor connection, too busy"
|
2016-11-22 02:03:29 +00:00
|
|
|
|
2016-12-09 20:03:25 +00:00
|
|
|
-- How many clients to serve at a time, maximum. This is to avoid DOS attacks.
|
2016-11-22 02:03:29 +00:00
|
|
|
maxConnections :: Int
|
2016-12-09 20:03:25 +00:00
|
|
|
maxConnections = 100
|
2016-11-22 02:03:29 +00:00
|
|
|
|
2016-11-30 20:38:16 +00:00
|
|
|
serveClient :: TransportHandle -> UUID -> Repo -> TBQueue Handle -> IO ()
|
2016-12-09 19:08:54 +00:00
|
|
|
serveClient th u r q = bracket setup cleanup start
|
2016-11-22 02:03:29 +00:00
|
|
|
where
|
2016-12-09 19:08:54 +00:00
|
|
|
setup = do
|
|
|
|
h <- atomically $ readTBQueue q
|
2016-12-08 21:17:01 +00:00
|
|
|
debugM "remotedaemon" "serving a Tor connection"
|
2016-12-09 19:08:54 +00:00
|
|
|
return h
|
|
|
|
|
|
|
|
cleanup h = do
|
|
|
|
debugM "remotedaemon" "done with Tor connection"
|
|
|
|
hClose h
|
|
|
|
|
|
|
|
start h = do
|
2016-12-02 17:50:56 +00:00
|
|
|
-- Avoid doing any work in the liftAnnex, since only one
|
|
|
|
-- can run at a time.
|
|
|
|
st <- liftAnnex th dupState
|
|
|
|
((), st') <- Annex.run st $ do
|
|
|
|
-- Load auth tokens for every connection, to notice
|
|
|
|
-- when the allowed set is changed.
|
|
|
|
allowed <- loadP2PAuthTokens
|
2016-12-06 19:40:31 +00:00
|
|
|
let conn = P2PConnection
|
|
|
|
{ connRepo = r
|
|
|
|
, connCheckAuth = (`isAllowedAuthToken` allowed)
|
|
|
|
, connIhdl = h
|
|
|
|
, connOhdl = h
|
2016-12-02 17:50:56 +00:00
|
|
|
}
|
2016-12-09 20:02:43 +00:00
|
|
|
v <- liftIO $ runNetProto conn $ P2P.serveAuth u
|
2016-12-02 19:34:15 +00:00
|
|
|
case v of
|
2016-12-09 19:08:54 +00:00
|
|
|
Right (Just theiruuid) -> authed conn theiruuid
|
2016-12-08 19:56:36 +00:00
|
|
|
Right Nothing -> liftIO $
|
2016-12-08 21:17:01 +00:00
|
|
|
debugM "remotedaemon" "Tor connection failed to authenticate"
|
2016-12-08 19:56:36 +00:00
|
|
|
Left e -> liftIO $
|
2016-12-09 17:00:19 +00:00
|
|
|
debugM "remotedaemon" ("Tor connection error before authentication: " ++ e)
|
2016-12-02 17:50:56 +00:00
|
|
|
-- Merge the duplicated state back in.
|
|
|
|
liftAnnex th $ mergeState st'
|
2016-12-09 19:08:54 +00:00
|
|
|
|
|
|
|
authed conn theiruuid =
|
|
|
|
bracket watchChangedRefs (liftIO . stopWatchingChangedRefs) $ \crh -> do
|
|
|
|
v' <- runFullProto (Serving theiruuid crh) conn $
|
2016-12-09 20:02:43 +00:00
|
|
|
P2P.serveAuthed u
|
2016-12-09 19:08:54 +00:00
|
|
|
case v' of
|
|
|
|
Right () -> return ()
|
|
|
|
Left e -> liftIO $ debugM "remotedaemon" ("Tor connection error: " ++ e)
|
2016-12-09 20:02:43 +00:00
|
|
|
|
|
|
|
-- Connect to peer's tor hidden service.
|
|
|
|
transport :: Transport
|
|
|
|
transport (RemoteRepo r _) url@(RemoteURI uri) th ichan ochan =
|
|
|
|
case unformatP2PAddress (show uri) of
|
|
|
|
Nothing -> return ()
|
|
|
|
Just addr -> robustConnection 1 $ do
|
|
|
|
g <- liftAnnex th Annex.gitRepo
|
|
|
|
bracket (connectPeer g addr) closeConnection (go addr)
|
|
|
|
where
|
|
|
|
go addr conn = do
|
|
|
|
myuuid <- liftAnnex th getUUID
|
|
|
|
authtoken <- fromMaybe nullAuthToken
|
|
|
|
<$> liftAnnex th (loadP2PRemoteAuthToken addr)
|
|
|
|
res <- runNetProto conn $
|
|
|
|
P2P.auth myuuid authtoken
|
|
|
|
case res of
|
|
|
|
Right (Just theiruuid)
|
|
|
|
| getUncachedUUID r == theiruuid -> do
|
|
|
|
send (CONNECTED url)
|
|
|
|
status <- handlecontrol
|
|
|
|
`race` handlepeer conn
|
|
|
|
send (DISCONNECTED url)
|
|
|
|
return $ either id id status
|
|
|
|
| otherwise -> return ConnectionStopping
|
|
|
|
_ -> return ConnectionClosed
|
|
|
|
|
|
|
|
send msg = atomically $ writeTChan ochan msg
|
|
|
|
|
|
|
|
handlecontrol = do
|
|
|
|
msg <- atomically $ readTChan ichan
|
|
|
|
case msg of
|
|
|
|
STOP -> return ConnectionStopping
|
|
|
|
LOSTNET -> return ConnectionStopping
|
|
|
|
_ -> handlecontrol
|
|
|
|
|
|
|
|
handlepeer conn = do
|
|
|
|
v <- runNetProto conn P2P.notifyChange
|
|
|
|
case v of
|
|
|
|
Right (Just (ChangedRefs shas)) -> do
|
|
|
|
whenM (checkNewShas th shas) $
|
|
|
|
fetch
|
|
|
|
handlepeer conn
|
|
|
|
_ -> return ConnectionClosed
|
|
|
|
|
|
|
|
fetch = do
|
|
|
|
send (SYNCING url)
|
|
|
|
ok <- inLocalRepo th $
|
|
|
|
runBool [Param "fetch", Param $ Git.repoDescribe r]
|
|
|
|
send (DONESYNCING url ok)
|