{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-} -- | This module provides the main interface for establishing secure and -- anonymous connections with other hosts on the interface using the -- Tor project. For more information about the Tor network, see: -- -- module Network.Anonymous.Tor ( -- * Introduction to Tor -- $tor-introduction -- * Client side -- $tor-client P.connect , P.connect' -- * Server side -- $tor-server , P.mapOnion , accept -- * Probing Tor configuration information , P.detectPort , P.socksPort -- ** Setting up the context , withSession ) where import Control.Concurrent (forkIO, threadDelay) import Control.Monad.IO.Class import qualified Data.Base32String.Default as B32 import qualified Network.Simple.TCP as NST import qualified Network.Socket as Network import qualified Network.Anonymous.Tor.Protocol as P -------------------------------------------------------------------------------- -- $tor-introduction -- -- This module is a (partial) implementation of the Tor control protocol. Tor is an -- internet anonimization network. Whereas historically, Tor is primarily -- intended for privately browsing the world wide web, the service also supports -- application oriented P2P communication, to implement communication between applications. -- -- The general idea of the Tor control interface to Tor is that you establish a master -- connection with the Tor control port, and create new, short-lived connections with -- the Tor bridge for the communication with the individual peers. -- -------------------------------------------------------------------------------- -- $tor-client -- -- == Connect through Tor with explicit port -- Connect through Tor on using a specified SOCKS port. Note that you do not -- need to authorize with the Tor control port for this functionality. -- -- @ -- main = 'connect' 9050 constructDestination worker -- -- where -- constructDestination = -- 'SocksT.SocksAddress' (SocksT.SocksAddrDomainName (BS8.pack "www.google.com")) 80 -- -- worker sock = -- -- Now you may use sock to communicate with the remote. -- return () -- @ -- -- == Connect through Tor using control port -- Connect through Tor and derive the SOCKS port from the Tor configuration. This -- function will query the Tor control service to find out which SOCKS port the -- Tor daemon listens at. -- -- @ -- main = 'withSession' withinSession -- -- where -- constructDestination = -- 'SocksT.SocksAddress' (SocksT.SocksAddrDomainName (BS8.pack "2a3b4c.onion")) 80 -- -- withinSession :: 'Network.Socket' -> IO () -- withinSession sock = do -- 'connect'' sock constructDestination worker -- -- worker sock = -- -- Now you may use sock to communicate with the remote. -- return () -- @ -- -------------------------------------------------------------------------------- -- $tor-server -- -- == Mapping -- Create a new hidden service, and map remote port 80 to local port 8080. -- -- @ -- main = 'withSession' withinSession -- -- where -- withinSession :: 'Network.Socket' -> IO () -- withinSession sock = do -- onion <- 'mapOnion' sock 80 8080 -- -- At this point, 'onion' contains the base32 representation of -- -- our hidden service, without the trailing '.onion' part. -- -- -- -- Remember that, once we leave this function, the connection with -- -- the Tor control service will be lost and any mappings will be -- -- cleaned up. -- @ -- -- == Server -- Convenience function which creates a hidden service on port 80 that is mapped -- to a server we create on the fly. Note that because we are mapping the hidden -- service's port 1:1 with our local port, port 80 must still be available. -- -- @ -- main = 'withSession' withinSession -- -- where -- withinSession :: 'Network.Socket' -> IO () -- withinSession sock = do -- onion <- 'accept' sock 80 worker -- -- At this point, 'onion' contains the base32 representation of -- -- our hidden service, without the trailing '.onion' part, and any -- -- incoming connections will be redirected to our 'worker' function. -- -- -- -- Once again, when we leave this function, all registered mappings -- -- will be lost. -- -- worker sock = do -- -- Now you may use sock to communicate with the remote. -- return () -- @ -------------------------------------------------------------------------------- -- | Establishes a connection and authenticates with the Tor control socket. -- After authorization has been succesfully completed it executes the callback -- provided. -- -- Note that when the session completes, the connection with the Tor control -- port is dropped, which means that any port mappings, connections and hidden -- services you have registered within the session will be cleaned up. This -- is by design, to prevent stale mappings when an application crashes. withSession :: Integer -- | Port the Tor control server is listening at. Use -- 'detectPort' to probe possible ports. -> (Network.Socket -> IO a) -- | Callback function called after a session has been -- established succesfully. -> IO a -- | Returns the value returned by the callback. withSession port callback = NST.connect "127.0.0.1" (show port) (\(sock, _) -> do _ <- P.authenticate sock callback sock) -- | Convenience function that creates a new hidden service and starts accepting -- connections for it. Note that this creates a new local server at the same -- port as the public port, so ensure that the port is not yet in use. accept :: MonadIO m => Network.Socket -- | Connection with Tor control server -> Integer -- | Port to listen at -> (Network.Socket -> IO ()) -- | Callback function called for each incoming connection -> m B32.Base32String -- | Returns the hidden service descriptor created -- without the '.onion' part. accept sock port callback = do -- First create local service _ <- liftIO $ forkIO $ NST.listen "*" (show port) (\(lsock, _) -> do putStrLn ("started server on port " ++ show port ++ ", now accepting connections") NST.accept lsock (\(csock, _) -> do putStrLn "accepted connection, now executing callback" _ <- callback csock threadDelay 1000000 return ())) P.mapOnion sock port port