{-# LANGUAGE ExistentialQuantification #-} -- |The high-level interface to the Tor implementation. Basic usage, for -- using Tor as a mechanism for connecting to Internet services anonymously: -- -- @ -- main :: IO () -- main = -- do tor <- startTor systemNetworkStack defaultTorOptions -- addrs <- torResolveName tor "hostname.com" -- case addrs of -- [] -> ... -- ((x, _):_) -> -- do sock <- torConnect tor x 80 -- torWrite sock ... -- resp <- torRead sock 1024 -- ... -- @ -- -- HaLVM users should initialize a HaNS network stack, and use that instead of -- systemNetworkStack, above. module Tor( -- * Setup and initialization Tor , startTor -- * Options , module Tor.Options -- * Functions for Tor entrance nodes , TorAddress(..) , RelayEndReason(..) , torResolveName , TorSocket , torConnect , torClose , torWrite , torRead ) where import Control.Exception import Control.Monad import Data.Maybe import Data.Word import Network.TLS import System.Timeout import Tor.Circuit import Tor.DataFormat.RelayCell import Tor.DataFormat.TorAddress import Tor.NetworkStack hiding (connect) import Tor.Options import Tor.State.CircuitManager import Tor.State.Credentials import Tor.State.Directories import Tor.State.LinkManager import Tor.State.Routers type HostName = String -- |A handle to the current Tor system state. data Tor = forall ls s . HasBackend s => Tor (CircuitManager ls s) -- |Start up the underlying Tor system, given a network stack to operate in and -- some setup options. startTor :: HasBackend s => TorNetworkStack ls s -> TorOptions -> IO Tor startTor ns o = do creds <- newCredentials o dirDB <- newDirectoryDatabase ns (torLog o) routerDB <- newRouterDatabase ns dirDB (torLog o) lm <- newLinkManager o ns routerDB creds cm <- newCircuitManager o ns creds routerDB lm when (not isRelay && isExit) $ do torLog o "WARNING: Requested exit without relay support: weird." torLog o "WARNING: Please check that this is really what you want." let res = Tor cm when (isRelay || isExit) $ handle (checkPublicFail o) $ do _ <- torResolveName res "google.com" -- not important addrs <- getAddresses creds torLog o ("I believe I have the following addrs: " ++ show addrs) fin <- timeout (15 * 1000000) $ tryConnect res orPort addrs unless (isJust fin) $ fail "Timeout connecting to myself." torLog o ("At least one of which is routable. Starting relay.") (_, PrivKeyRSA pkey) <- getSigningKey creds desc <- getRouterDesc creds sendRouterDescription ns (torLog o) dirDB desc pkey return (Tor cm) where isRelay = isJust (torRelayOptions o) isExit = isJust (torExitOptions o) orPort = maybe 9374 torOnionPort (torRelayOptions o) tryConnect :: Tor -> Word16 -> [TorAddress] -> IO () tryConnect _ _ [] = fail "Could not connect to any addresses." tryConnect tor p (x:rest) = handle failRecurse $ do con <- torConnect tor x p torClose con ReasonDone where failRecurse :: SomeException -> IO () failRecurse _ = tryConnect tor p rest checkPublicFail :: TorOptions -> SomeException -> IO () checkPublicFail o _ = torLog o ("Failed to create connection to myself. No relay/exit support.") -- ----------------------------------------------------------------------------- -- |Resolve the given host name, anonymously. This routine will create a new -- circuit unless torMaxCircuits has been reached, at which point it will re-use -- an existing circuit. torResolveName :: Tor -> HostName -> IO [(TorAddress, Word32)] torResolveName (Tor cm) name = do circ <- openCircuit cm [ExitNode] resolveName circ name -- |Connect to the given address via Tor. The TorAddress should be one of the -- IP4 or IP6 variants, rather than a hostname or an error. The result will be -- the built circuit. This will throw exceptions if failures occur during the -- building process. torConnect :: Tor -> TorAddress -> Word16 -> IO TorSocket torConnect (Tor cm) addr port = do circ <- openCircuit cm [ExitNodeAllowing addr port] connectToHost circ addr port