git-annex/RemoteDaemon/Transport/Tor.hs

97 lines
2.6 KiB
Haskell
Raw Normal View History

2016-11-20 19:45:01 +00:00
{- git-remote-daemon, tor hidden service transport
-
- Copyright 2016 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module RemoteDaemon.Transport.Tor (server) where
import Common
import qualified Annex
import Annex.Concurrent
2016-11-20 19:45:01 +00:00
import RemoteDaemon.Types
import RemoteDaemon.Common
import Utility.Tor
import Utility.FileMode
import Utility.AuthToken
import Remote.Helper.Tor
2016-11-24 20:36:16 +00:00
import P2P.Protocol
import P2P.Annex
import P2P.Auth
2016-11-20 19:45:01 +00:00
import Annex.UUID
import Types.UUID
import Messages
import Git
2016-11-20 19:45:01 +00:00
import System.PosixCompat.User
import Network.Socket
import Control.Concurrent
import System.Log.Logger (debugM)
import Control.Concurrent.STM
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
q <- newTBQueueIO maxConnections
replicateM_ maxConnections $
forkIO $ forever $ serveClient th u r q
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
soc <- socket AF_UNIX Stream defaultProtocol
bind soc (SockAddrUnix sock)
-- 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]
listen soc 2
debugM "remotedaemon" "tor hidden service running"
forever $ do
(conn, _) <- accept soc
h <- torHandle conn
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
warningIO "dropped TOR connection, too busy"
-- How many clients to serve at a time, maximum. This is to avoid DOS
-- attacks.
maxConnections :: Int
maxConnections = 10
serveClient :: TransportHandle -> UUID -> Repo -> TBQueue Handle -> IO ()
serveClient th u r q = bracket setup cleanup go
where
setup = atomically $ readTBQueue q
cleanup = hClose
go h = do
debugM "remotedaemon" "serving a TOR connection"
-- 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
let runenv = RunEnv
{ runRepo = r
, runCheckAuth = (`isAllowedAuthToken` allowed)
, runIhdl = h
, runOhdl = h
}
void $ runFullProto runenv (serve u)
-- Merge the duplicated state back in.
liftAnnex th $ mergeState st'
debugM "remotedaemon" "done with TOR connection"