{-# LANGUAGE ScopedTypeVariables #-}
module System.IO.Streams.TLS
( TLSConnection
, connect
, connectTLS
, tLsToConnection
, accept
, module Data.TLSSetting
) where
import qualified Control.Exception as E
import Data.Connection
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import Data.TLSSetting
import qualified Network.Socket as N
import Network.TLS (ClientParams, Context, ServerParams)
import qualified Network.TLS as TLS
import qualified System.IO.Streams as Stream
import qualified System.IO.Streams.TCP as TCP
type TLSConnection = Connection (TLS.Context, N.SockAddr)
tLsToConnection :: (Context, N.SockAddr)
-> IO TLSConnection
tLsToConnection :: (Context, SockAddr) -> IO TLSConnection
tLsToConnection (Context
ctx, SockAddr
addr) = do
InputStream ByteString
is <- forall a. IO (Maybe a) -> IO (InputStream a)
Stream.makeInputStream IO (Maybe ByteString)
input
forall (m :: * -> *) a. Monad m => a -> m a
return (forall a.
InputStream ByteString
-> (ByteString -> IO ()) -> IO () -> a -> Connection a
Connection InputStream ByteString
is forall {m :: * -> *}. MonadIO m => ByteString -> m ()
write (Context -> IO ()
closeTLS Context
ctx) (Context
ctx, SockAddr
addr))
where
input :: IO (Maybe ByteString)
input = (do
ByteString
s <- forall (m :: * -> *). MonadIO m => Context -> m ByteString
TLS.recvData Context
ctx
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$! if ByteString -> Bool
B.null ByteString
s then forall a. Maybe a
Nothing else forall a. a -> Maybe a
Just ByteString
s
) forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`E.catch` (\(SomeException
_::E.SomeException) -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing)
write :: ByteString -> m ()
write ByteString
s = forall (m :: * -> *). MonadIO m => Context -> ByteString -> m ()
TLS.sendData Context
ctx ByteString
s
closeTLS :: Context -> IO ()
closeTLS :: Context -> IO ()
closeTLS Context
ctx = (forall (m :: * -> *). MonadIO m => Context -> m ()
TLS.bye Context
ctx forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Context -> IO ()
TLS.contextClose Context
ctx)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`E.catch` (\(SomeException
_::E.SomeException) -> forall (m :: * -> *) a. Monad m => a -> m a
return ())
connectTLS :: ClientParams
-> Maybe String
-> N.HostName
-> N.PortNumber
-> IO (Context, N.SockAddr)
connectTLS :: ClientParams
-> Maybe String -> String -> PortNumber -> IO (Context, SockAddr)
connectTLS ClientParams
prms Maybe String
subname String
host PortNumber
port = do
let subname' :: String
subname' = forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
host forall a. a -> a
id Maybe String
subname
prms' :: ClientParams
prms' = ClientParams
prms { clientServerIdentification :: (String, ByteString)
TLS.clientServerIdentification = (String
subname', String -> ByteString
BC.pack (forall a. Show a => a -> String
show PortNumber
port)) }
(Socket
sock, SockAddr
addr) <- String -> PortNumber -> IO (Socket, SockAddr)
TCP.connectSocket String
host PortNumber
port
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracketOnError (forall (m :: * -> *) backend params.
(MonadIO m, HasBackend backend, TLSParams params) =>
backend -> params -> m Context
TLS.contextNew Socket
sock ClientParams
prms') Context -> IO ()
closeTLS forall a b. (a -> b) -> a -> b
$ \ Context
ctx -> do
forall (m :: * -> *). MonadIO m => Context -> m ()
TLS.handshake Context
ctx
forall (m :: * -> *) a. Monad m => a -> m a
return (Context
ctx, SockAddr
addr)
connect :: ClientParams
-> Maybe String
-> N.HostName
-> N.PortNumber
-> IO TLSConnection
connect :: ClientParams
-> Maybe String -> String -> PortNumber -> IO TLSConnection
connect ClientParams
prms Maybe String
subname String
host PortNumber
port = ClientParams
-> Maybe String -> String -> PortNumber -> IO (Context, SockAddr)
connectTLS ClientParams
prms Maybe String
subname String
host PortNumber
port forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Context, SockAddr) -> IO TLSConnection
tLsToConnection
accept :: ServerParams
-> N.Socket
-> IO TLSConnection
accept :: ServerParams -> Socket -> IO TLSConnection
accept ServerParams
prms Socket
sock = do
(Socket
sock', SockAddr
addr) <- Socket -> IO (Socket, SockAddr)
N.accept Socket
sock
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracketOnError (forall (m :: * -> *) backend params.
(MonadIO m, HasBackend backend, TLSParams params) =>
backend -> params -> m Context
TLS.contextNew Socket
sock' ServerParams
prms) Context -> IO ()
closeTLS forall a b. (a -> b) -> a -> b
$ \ Context
ctx -> do
forall (m :: * -> *). MonadIO m => Context -> m ()
TLS.handshake Context
ctx
TLSConnection
conn <- (Context, SockAddr) -> IO TLSConnection
tLsToConnection (Context
ctx, SockAddr
addr)
forall (m :: * -> *) a. Monad m => a -> m a
return TLSConnection
conn