pipes-network-tls-0.1.0.0: TLS-secured network connections support for pipes.

Safe HaskellNone

Control.Proxy.TCP.TLS.Safe

Contents

Description

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.

Synopsis

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.

connectSource

Arguments

:: (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.

makeClientSettings

Arguments

:: [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 getSystemCertificateStore to obtain the operating system's defaults.

-> 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.

connectReadSSource

Arguments

:: 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

connectWriteDSource

Arguments

:: 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.

serveSource

Arguments

:: (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.

makeServerSettings

Arguments

:: 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 and TLS12
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

listen

Arguments

:: (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

acceptSource

Arguments

:: (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.

acceptForkSource

Arguments

:: (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.

serveReadSSource

Arguments

:: 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

serveWriteDSource

Arguments

:: 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 Proxys to interact with the other connection end using pipes streams.

contextReadSSource

Arguments

:: 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.

contextWriteDSource

Arguments

:: 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.

Constructors

HostAny

Any available host.

HostIPv4

Any available IPv4 host.

HostIPv6

Any available IPv6 host.

Host HostName

An explicit host name.

Instances

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.

Constructors

Credential !X509 !PrivateKey [X509] 

Instances

data Timeout

Exception thrown when a timeout has elapsed.

Constructors

Timeout String

Timeouted with an additional explanatory message.