haskell-tor-0.1.0.0: src/Tor/NetworkStack.hs
{-# LANGUAGE ExistentialQuantification #-}
-- |Defines the network API required for a Tor implementation to run.
module Tor.NetworkStack(
TorNetworkStack(..)
, SomeNetworkStack(..)
, toBackend
, recvAll
, recvLine
)
where
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import Data.Word
import Network.TLS
import Tor.DataFormat.TorAddress
-- |A network stack, but with the type variables hidden.
data SomeNetworkStack = forall lsock sock . HasBackend sock =>
MkNS (TorNetworkStack lsock sock)
-- |The type of a Tor-compatible network stack. The first type variable is the
-- type of a listener socket, the second the type of a standard connection
-- socket.
data TorNetworkStack lsock sock = TorNetworkStack {
connect :: String -> Word16 -> IO (Maybe sock)
-- |Lookup the given hostname and return any IP6 (Left) or IP4 (Right)
-- addresses associated with it.
, getAddress :: String -> IO [TorAddress]
, listen :: Word16 -> IO lsock
, accept :: lsock -> IO (sock, TorAddress)
, recv :: sock -> Int -> IO S.ByteString
, write :: sock -> L.ByteString -> IO ()
, flush :: sock -> IO ()
, close :: sock -> IO ()
, lclose :: lsock -> IO ()
}
-- |Receive a line of ASCII text from a socket.
recvLine :: TorNetworkStack ls s -> s -> IO L.ByteString
recvLine ns s = go []
where
go acc =
do next <- recv ns s 1
case S.uncons next of
Nothing -> return (L.pack (reverse acc))
Just (10, _) -> return (L.pack (reverse acc))
Just (f, _) -> go (f:acc)
-- |Receive all the input from the socket as a lazy ByteString; this may cause
-- the system to block upon some ByteString operations to fetch more data.
recvAll :: TorNetworkStack ls s -> s -> IO L.ByteString
recvAll ns s = go []
where
go acc =
do next <- recv ns s 4096
if S.null next
then return (L.fromChunks (reverse acc))
else go (next:acc)
-- |Convert a Tor-compatible network stack to a TLS-compatible Backend
-- structure.
toBackend :: TorNetworkStack ls s -> s -> Backend
toBackend ns s = Backend {
backendFlush = flush ns s
, backendClose = close ns s
, backendRecv = recv ns s
, backendSend = write ns s . L.fromStrict
}