haskell-tor-0.1.0.0: src/Tor/NetworkStack/System.hs
{-# LANGUAGE CPP #-}
-- |Routines for integrating Tor with the standard network library.
module Tor.NetworkStack.System(systemNetworkStack) where
import Data.Binary.Put
import Data.ByteString(ByteString)
import Data.ByteString.Lazy(toStrict)
import qualified Data.ByteString as BS
import Data.Word
import Network(listenOn, PortID(..))
import Network.BSD
import Network.Socket as Sys hiding (recv)
import Network.Socket.ByteString.Lazy(sendAll)
import qualified Network.Socket.ByteString as Sys
import Tor.DataFormat.TorAddress
import Tor.NetworkStack
-- |A Tor-compatible network stack that uses the 'network' library.
systemNetworkStack :: TorNetworkStack Socket Socket
systemNetworkStack = TorNetworkStack {
Tor.NetworkStack.connect = systemConnect
, Tor.NetworkStack.getAddress = systemLookup
, Tor.NetworkStack.listen = systemListen
, Tor.NetworkStack.accept = systemAccept
, Tor.NetworkStack.recv = systemRead
, Tor.NetworkStack.write = sendAll
, Tor.NetworkStack.flush = const (return ())
, Tor.NetworkStack.close = Sys.close
, Tor.NetworkStack.lclose = Sys.close
}
systemConnect :: String -> Word16 -> IO (Maybe Socket)
systemConnect addrStr port =
do let ainfo = defaultHints { addrFamily = AF_INET, addrSocketType = Stream }
hname = addrStr
sname = show port
addrinfos <- getAddrInfo (Just ainfo) (Just hname) (Just sname)
case addrinfos of
[] -> return Nothing
(x:_) ->
do sock <- socket AF_INET Stream defaultProtocol
Sys.connect sock (addrAddress x)
return (Just sock)
systemLookup :: String -> IO [TorAddress]
systemLookup hostname =
-- FIXME: Tack the hostname on the end, as a default?
do res <- getAddrInfo Nothing (Just hostname) Nothing
return (map (convertAddress . addrAddress) res)
systemListen :: Word16 -> IO Socket
systemListen port = listenOn (PortNumber (fromIntegral port))
convertAddress :: SockAddr -> TorAddress
convertAddress (SockAddrInet _ x) =
IP4 (ip4ToString (toStrict (runPut (putWord32be x))))
convertAddress (SockAddrInet6 _ _ (a,b,c,d) _) =
IP6 (ip6ToString (toStrict (runPut (mapM_ putWord32be [a,b,c,d]))))
convertAddress x =
error ("Incompatible address type: " ++ show x)
systemAccept :: Socket -> IO (Socket, TorAddress)
systemAccept lsock =
do (res, addr) <- Sys.accept lsock
return (res, convertAddress addr)
systemRead :: Socket -> Int -> IO ByteString
systemRead _ 0 = return BS.empty
systemRead sock amt =
do start <- Sys.recv sock (fromIntegral amt)
let left = fromIntegral (amt - fromIntegral (BS.length start))
if BS.null start
then return BS.empty
else (start `BS.append`) `fmap` systemRead sock left