Safe Haskell | None |
---|
This module exports functions that allow you to use TLS-secured
TCP connections as Proxy
streams, as well as utilities to connect to a
TLS-enabled TCP server or running your own, possibly within the pipeline
itself by relying on the facilities provided by ExceptionP
from the
pipes-safe
library.
If you don't need to establish new TLS connections within your pipeline, then consider using the simpler and similar functions exported by Control.Proxy.TCP.TLS.
This module re-exports many functions and types from Network.Simple.TCP.TLS
module in the network-simple
package. You might refer to that module for
more documentation.
- connect :: (Proxy p, Monad m) => (forall x. SafeIO x -> m x) -> ClientSettings -> HostName -> ServiceName -> ((Context, SockAddr) -> ExceptionP p a' a b' b m r) -> ExceptionP p a' a b' b m r
- data ClientSettings
- getDefaultClientSettings :: IO ClientSettings
- makeClientSettings :: [Credential] -> Maybe HostName -> CertificateStore -> ClientSettings
- connectReadS :: Proxy p => Maybe Int -> ClientSettings -> HostName -> ServiceName -> () -> Producer (ExceptionP p) ByteString SafeIO ()
- connectWriteD :: Proxy p => Maybe Int -> ClientSettings -> HostName -> ServiceName -> x -> ExceptionP p x ByteString x ByteString SafeIO r
- serve :: (Proxy p, Monad m) => (forall x. SafeIO x -> m x) -> ServerSettings -> HostPreference -> ServiceName -> ((Context, SockAddr) -> IO ()) -> ExceptionP p a' a b' b m r
- data ServerSettings
- makeServerSettings :: Credential -> Maybe CertificateStore -> ServerSettings
- listen :: (Proxy p, Monad m) => (forall x. SafeIO x -> m x) -> HostPreference -> ServiceName -> ((Socket, SockAddr) -> ExceptionP p a' a b' b m r) -> ExceptionP p a' a b' b m r
- accept :: (Proxy p, Monad m) => (forall x. SafeIO x -> m x) -> ServerSettings -> Socket -> ((Context, SockAddr) -> ExceptionP p a' a b' b m r) -> ExceptionP p a' a b' b m r
- acceptFork :: (Proxy p, Monad m) => (forall x. SafeIO x -> m x) -> ServerSettings -> Socket -> ((Context, SockAddr) -> IO ()) -> ExceptionP p a' a b' b m ThreadId
- serveReadS :: Proxy p => Maybe Int -> ServerSettings -> HostPreference -> ServiceName -> () -> Producer (ExceptionP p) ByteString SafeIO ()
- serveWriteD :: Proxy p => Maybe Int -> ServerSettings -> HostPreference -> ServiceName -> x -> ExceptionP p x ByteString x ByteString SafeIO r
- contextReadS :: Proxy p => Maybe Int -> Context -> () -> Producer (ExceptionP p) ByteString SafeIO ()
- contextWriteD :: Proxy p => Maybe Int -> Context -> x -> ExceptionP p x ByteString x ByteString SafeIO r
- data HostPreference
- data Credential = Credential !X509 !PrivateKey [X509]
- data Timeout = Timeout String
Client side
Here's how you could run a simple TLS-secured TCP client:
import Control.Proxy.TCP.TLS.Safe settings <- getDefaultClientSettings connect settings "www.example.org" "443" $ \(tlsCtx, remoteAddr) -> do tryIO . putStrLn $ "Secure connection established to " ++ show remoteAddr -- now you may use tlsCtx as you please within this scope, possibly with -- the contextReadS or contextWriteD proxies explained below.
You might prefer to use the simpler but less general solutions offered by
connectReadS
and connectWriteD
, so check those too.
:: (Proxy p, Monad m) | |
=> (forall x. SafeIO x -> m x) | Monad morphism. |
-> ClientSettings | TLS settings. |
-> HostName | Server hostname. |
-> ServiceName | Server service port. |
-> ((Context, SockAddr) -> ExceptionP p a' a b' b m r) | Computation to run in a different thread once a TLS-secured connection is established. Takes the TLS connection context and remote end address. |
-> ExceptionP p a' a b' b m r |
Connect to a TLS-secured TCP server and use the connection.
A TLS handshake is performed immediately after establishing the TCP connection.
The connection is properly closed when done or in case of exceptions. If you
need to manage the lifetime of the connection resources yourself, then use
connectTls
instead.
data ClientSettings
Abstract type representing the configuration settings for a TLS client.
Use makeClientSettings
or getDefaultClientSettings
to obtain your
ClientSettings
value.
getDefaultClientSettings :: IO ClientSettings
Get the system default ClientSettings
.
See makeClientSettings
for the for the default TLS settings used.
:: [Credential] | Credentials to provide to the server, if requested. The first one is used in case we can't choose one based on information provided by the server. |
-> Maybe HostName | Explicit Server Name Identification (SNI). |
-> CertificateStore | CAs used to verify the server certificate.
Use |
-> ClientSettings |
Make defaults ClientSettings
.
The following TLS settings are used by default:
- Supported versions
-
TLS10
,TLS11
,TLS12
. - Version reported during ClientHello
-
TLS10
. - Supported cipher suites
- In decreasing order of preference:
cipher_AES256_SHA256
,cipher_AES256_SHA1
,cipher_AES128_SHA256
,cipher_AES128_SHA1
,cipher_RC4_128_SHA1
,cipher_RC4_128_MD5
.
Streaming
The following proxies allow you to easily connect to a TLS-secured TCP server and immediately interact with it using streams, all at once, instead of having to perform the individual steps separately.
:: Proxy p | |
=> Maybe Int | Optional timeout in microseconds (1/10^6 seconds). |
-> ClientSettings | TLS settings. |
-> HostName | |
-> ServiceName | Server service port. |
-> () | |
-> Producer (ExceptionP p) ByteString SafeIO () |
Connect to a TLS-secured TCP server and send downstream the decrypted bytes received from the remote end.
Up to 16384
decrypted bytes will be received at once. The TLS connection is
automatically renegotiated if a ClientHello message is received.
If an optional timeout is given and receiveing data from the remote end takes
more time that such timeout, then throw a Timeout
exception in the
ExceptionP
proxy transformer.
If the remote peer closes its side of the connection of EOF is reached, this proxy returns.
The connection is closed when done or in case of exceptions.
Using this proxy you can write code like the following, which prints whatever is received through a TLS-secured TCP connection to a given server listening at hostname example.org on port 4433:
>>>
settings <- getDefaultClientSettings
>>>
let src = connectReadS Nothing settings "www.example.org" "4433"
>>>
runSafeIO . runProxy . runEitherK $ src >-> try . printD
:: Proxy p | |
=> Maybe Int | Optional timeout in microseconds (1/10^6 seconds). |
-> ClientSettings | TLS settings. |
-> HostName | Server host name. |
-> ServiceName | Server service port. |
-> x | |
-> ExceptionP p x ByteString x ByteString SafeIO r |
Connects to a TLS-secured TCP server, encrypts and sends to the remote end the bytes received from upstream, then forwards such same bytes downstream.
Requests from downstream are forwarded upstream.
If an optional timeout is given and sending data to the remote end takes
more time that such timeout, then throw a Timeout
exception in the
ExceptionP
proxy transformer.
The connection is properly closed when done or in case of exceptions.
Using this proxy you can write code like the following, which sends data to a TLS-secured TCP server listening at hostname example.org on port 4433:
>>>
:set -XOverloadedStrings
>>>
settings <- getDefaultClientSettings
>>>
let dst = connectWriteS Nothing settings "www.example.org" "4433"
>>>
runSafeIO . runProxy . runEitherK $ fromListS ["He","llo\r\n"] >-> dst
Server side
Here's how you could run a simple TLS-secured TCP server that handles in
different threads each incoming connection to port 4433
at hostname
example.org
. You will need a X509 certificate and a private key appropiate
to be used at that hostname.
import Control.Proxy.TCP.TLS.Safe import Network.TLS.Extra (fileReadCertificate, fileReadPrivateKey) cert <- fileReadCertificate "~/example.org.crt" pkey <- fileReadPrivateKey "~/example.org.key" let cred = Credential cert pkey [] settings = makeServerSettings cred Nothing serve settings (Host "example.org") "4433" $ \(tlsCtx, remoteAddr) -> do tryIO . putStrLn $ "Secure connection established from " ++ show remoteAddr -- now you may use tlsCtx as you please within this scope, possibly with -- the contextReadS or contextWriteD proxies explained below.
You might prefer to use the simpler but less general solutions offered by
serveReadS
and serveWriteD
, or if you need to control the way your
server runs, then you can use more advanced functions such as listen
,
accept
and acceptFork
, so check those functions too.
:: (Proxy p, Monad m) | |
=> (forall x. SafeIO x -> m x) | Monad morphism. |
-> ServerSettings | TLS settings. |
-> HostPreference | Preferred host to bind. |
-> ServiceName | Service port to bind. |
-> ((Context, SockAddr) -> IO ()) | Computation to run in a different thread once an incomming connection is accepted and a TLS-secured communication is established. Takes the TLS connection context and remote end address. |
-> ExceptionP p a' a b' b m r |
Start a TLS-secured TCP server that accepts incoming connections and handles each of them concurrently, in different threads.
A TLS handshake is performed immediately after establishing each TCP connection.
Any acquired network resources are properly closed and discarded when done or in case of exceptions.
Note: This function binds a listening socket, accepts an connection, performs a TLS handshake and then safely closes the connection. You don't need to perform any of those steps manually.
data ServerSettings
Abstract type representing the configuration settings for a TLS server.
Use makeServerSettings
to obtain your ServerSettings
value, and
updateServerParams
to update it.
:: Credential | Server credential. |
-> Maybe CertificateStore | CAs used to verify the client certificate. If specified, then a valid client certificate will be expected during on handshake. |
-> ServerSettings |
Make default ServerSettings
.
The following TLS settings are used by default:
- Supported versions
-
TLS10
,TLS11
,TLS12
. - Supported cipher suites for
TLS10
-
In decreasing order of preference:
cipher_AES256_SHA256
,cipher_AES256_SHA1
,cipher_AES128_SHA256
,cipher_AES128_SHA1
,cipher_RC4_128_SHA1
,cipher_RC4_128_MD5
. The cipher suite preferred by the client is used. - Supported cipher suites for
TLS11
andTLS12
-
In decreasing order of preference:
cipher_AES256_SHA256
,cipher_AES256_SHA1
,cipher_AES128_SHA256
,cipher_AES128_SHA1
. The cipher suite preferred by the client is used.
Listening
:: (Proxy p, Monad m) | |
=> (forall x. SafeIO x -> m x) | Monad morphism. |
-> HostPreference | Preferred host to bind. |
-> ServiceName | Service port to bind. |
-> ((Socket, SockAddr) -> ExceptionP p a' a b' b m r) | Computation taking the listening socket and the address it's bound to. |
-> ExceptionP p a' a b' b m r |
Bind a TCP listening socket and use it.
The listening socket is closed when done or in case of exceptions.
If you prefer to acquire and close the socket yourself, then use
bindSock
and the listen
and sClose
functions from
Network.Socket instead.
Note: maxListenQueue
is tipically 128, which is too small for high
performance servers. So, we use the maximum between maxListenQueue
and
2048 as the default size of the listening queue.
Accepting
:: (Proxy p, Monad m) | |
=> (forall x. SafeIO x -> m x) | Monad morphism. |
-> ServerSettings | TLS settings. |
-> Socket | Listening and bound socket. |
-> ((Context, SockAddr) -> ExceptionP p a' a b' b m r) | Computation to run once an incomming connection is accepted and a TLS-secured communication is established. Takes the TLS connection context and remote end address. |
-> ExceptionP p a' a b' b m r |
Accept a single incoming TLS-secured TCP connection and use it.
A TLS handshake is performed immediately after establishing each TCP connection.
The connection properly closed when done or in case of exceptions.
:: (Proxy p, Monad m) | |
=> (forall x. SafeIO x -> m x) | Monad morphism. |
-> ServerSettings | TLS settings. |
-> Socket | Listening and bound socket. |
-> ((Context, SockAddr) -> IO ()) | Computation to run in a different thread once an incomming connection is accepted and a TLS-secured communication is established. Takes the TLS connection context and remote end address. |
-> ExceptionP p a' a b' b m ThreadId |
Like accept
, except it uses a different thread to performs the TLS
handshake and run the given computation.
Streaming
The following proxies allow you to easily run a TLS-secured TCP server and immediately interact with incoming connections using streams, all at once, instead of having to perform the individual steps separately.
:: Proxy p | |
=> Maybe Int | Optional timeout in microseconds (1/10^6 seconds). |
-> ServerSettings | TLS settings. |
-> HostPreference | Preferred host to bind. |
-> ServiceName | Service port to bind. |
-> () | |
-> Producer (ExceptionP p) ByteString SafeIO () |
Binds a listening TCP socket, accepts a single TLS-secured connection and sends downstream any decrypted bytes received from the remote end.
Up to 16384
decrypted bytes will be received at once. The TLS connection is
automatically renegotiated if a ClientHello message is received.
If an optional timeout is given and receiveing data from the remote end takes
more time that such timeout, then throw a Timeout
exception in the
ExceptionP
proxy transformer.
If the remote peer closes its side of the connection of EOF is reached, this proxy returns.
Both the listening and connection sockets are closed when done or in case of exceptions.
Using this proxy you can write code like the following, which prints data received from a TLS-secured TCP connection to the hostname example.org at port 4433:
>>>
import Network.TLS.Extra (fileReadCertificate, fileReadPrivateKey)
>>>
cert <- fileReadCertificate "~/example.org.crt"
>>>
pkey <- fileReadPrivateKey "~/example.org.key"
>>>
let settings = makeServerSettings cert pkey Nothing
>>>
let src = serveReadS Nothing settings (Host "example.org") "4433"
>>>
runSafeIO . runProxy . runEitherK $ src >-> try . printD
:: Proxy p | |
=> Maybe Int | Optional timeout in microseconds (1/10^6 seconds). |
-> ServerSettings | TLS settings. |
-> HostPreference | Preferred host to bind. |
-> ServiceName | Service port to bind. |
-> x | |
-> ExceptionP p x ByteString x ByteString SafeIO r |
Binds a listening TCP socket, accepts a single TLS-secured connection, sends to the remote end the bytes received from upstream and then forwards such sames bytesdownstream.
Requests from downstream are forwarded upstream.
If an optional timeout is given and sending data to the remote end takes
more time that such timeout, then throw a Timeout
exception in the
ExceptionP
proxy transformer.
If the remote peer closes its side of the connection, this proxy returns.
Both the listening and connection sockets are closed when done or in case of exceptions.
Using this proxy you can write straightforward code like the following, which sends data to an incoming TLS-secured TCP connection to the hostname example.org at port 4433:
>>>
:set -XOverloadedStrings
>>>
import Network.TLS.Extra (fileReadCertificate, fileReadPrivateKey)
>>>
cert <- fileReadCertificate "~/example.org.crt"
>>>
pkey <- fileReadPrivateKey "~/example.org.key"
>>>
let settings = makeServerSettings cert pkey Nothing
>>>
let dst = serveWriteD Nothing settings "example.org" "4433"
>>>
runSafeIO . runProxy . runEitherK $ fromListS ["He","llo\r\n"] >-> dst
Socket streams
Once you have a an established TLS Context
, you can use the following
Proxy
s to interact with the other connection end using pipes streams.
:: Proxy p | |
=> Maybe Int | Optional timeout in microseconds (1/10^6 seconds). |
-> Context | Established TLS connection context. |
-> () | |
-> Producer (ExceptionP p) ByteString SafeIO () |
Receives decrypted bytes from the remote end, sending them downstream.
Up to 16384
decrypted bytes will be received at once. The TLS connection is
automatically renegotiated if a ClientHello message is received.
If an optional timeout is given and receiveing data from the remote end takes
more time that such timeout, then throw a Timeout
exception in the
ExceptionP
proxy transformer.
If the remote peer closes its side of the connection or EOF is reached, this proxy returns.
:: Proxy p | |
=> Maybe Int | Optional timeout in microseconds (1/10^6 seconds). |
-> Context | Established TLS connection context. |
-> x | |
-> ExceptionP p x ByteString x ByteString SafeIO r |
Encrypts and sends to the remote end the bytes received from upstream, then forwards such same bytes downstream.
If an optional timeout is given and sending data to the remote end takes
more time that such timeout, then throw a Timeout
exception in the
ExceptionP
proxy transformer.
If the remote peer closes its side of the connection, this proxy returns.
Requests from downstream are forwarded upstream.
Exports
data HostPreference
Preferred host to bind.
HostAny | Any available host. |
HostIPv4 | Any available IPv4 host. |
HostIPv6 | Any available IPv6 host. |
Host HostName | An explicit host name. |
Eq HostPreference | |
Ord HostPreference | |
Read HostPreference | |
Show HostPreference | |
IsString HostPreference | The following special values are recognized: |
data Credential
Primary certificate, private key and the rest of the certificate chain.
Credential !X509 !PrivateKey [X509] |