2011-03-02 08:43:05 +00:00
|
|
|
{-# OPTIONS_HADDOCK hide #-}
|
2012-02-08 09:20:28 +00:00
|
|
|
{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
|
2011-03-01 20:01:40 +00:00
|
|
|
-- |
|
|
|
|
-- Module : Network.TLS.Core
|
|
|
|
-- License : BSD-style
|
|
|
|
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
|
|
|
|
-- Stability : experimental
|
|
|
|
-- Portability : unknown
|
|
|
|
--
|
|
|
|
module Network.TLS.Core
|
2012-03-27 07:57:51 +00:00
|
|
|
(
|
|
|
|
-- * Internal packet sending and receiving
|
|
|
|
sendPacket
|
|
|
|
, recvPacket
|
|
|
|
|
|
|
|
-- * Initialisation and Termination of context
|
|
|
|
, bye
|
|
|
|
, handshake
|
|
|
|
, HandshakeFailed(..)
|
|
|
|
, ConnectionNotEstablished(..)
|
|
|
|
|
|
|
|
-- * Next Protocol Negotiation
|
|
|
|
, getNegotiatedProtocol
|
|
|
|
|
|
|
|
-- * High level API
|
|
|
|
, sendData
|
|
|
|
, recvData
|
|
|
|
, recvData'
|
|
|
|
) where
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2011-12-06 00:15:00 +00:00
|
|
|
import Network.TLS.Context
|
2011-03-01 20:01:40 +00:00
|
|
|
import Network.TLS.Struct
|
2012-04-27 06:21:29 +00:00
|
|
|
import Network.TLS.IO
|
2012-04-27 06:28:17 +00:00
|
|
|
import Network.TLS.Handshake
|
2012-03-15 08:59:04 +00:00
|
|
|
import qualified Network.TLS.State as S
|
2011-03-01 20:01:40 +00:00
|
|
|
import qualified Data.ByteString as B
|
2012-02-08 09:20:28 +00:00
|
|
|
import Data.ByteString.Char8 ()
|
2011-03-01 20:01:40 +00:00
|
|
|
import qualified Data.ByteString.Lazy as L
|
2011-03-01 20:01:40 +00:00
|
|
|
|
|
|
|
import Control.Monad.State
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- | notify the context that this side wants to close connection.
|
|
|
|
-- this is important that it is called before closing the handle, otherwise
|
|
|
|
-- the session might not be resumable (for version < TLS1.2).
|
|
|
|
--
|
|
|
|
-- this doesn't actually close the handle
|
2012-03-15 07:53:03 +00:00
|
|
|
bye :: MonadIO m => Context -> m ()
|
2011-06-10 20:24:46 +00:00
|
|
|
bye ctx = sendPacket ctx $ Alert [(AlertLevel_Warning, CloseNotify)]
|
2011-03-01 20:01:40 +00:00
|
|
|
|
2012-02-12 18:59:19 +00:00
|
|
|
-- | If the Next Protocol Negotiation extension has been used, this will
|
|
|
|
-- return get the protocol agreed upon.
|
2012-03-15 08:59:04 +00:00
|
|
|
getNegotiatedProtocol :: MonadIO m => Context -> m (Maybe B.ByteString)
|
2012-02-12 18:59:19 +00:00
|
|
|
getNegotiatedProtocol ctx = usingState_ ctx S.getNegotiatedProtocol
|
|
|
|
|
2011-03-02 08:43:05 +00:00
|
|
|
-- | sendData sends a bunch of data.
|
|
|
|
-- It will automatically chunk data to acceptable packet size
|
2012-03-15 07:53:03 +00:00
|
|
|
sendData :: MonadIO m => Context -> L.ByteString -> m ()
|
2012-01-25 16:03:31 +00:00
|
|
|
sendData ctx dataToSend = checkValid ctx >> mapM_ sendDataChunk (L.toChunks dataToSend)
|
2012-03-27 07:57:51 +00:00
|
|
|
where sendDataChunk d
|
|
|
|
| B.length d > 16384 = do
|
|
|
|
let (sending, remain) = B.splitAt 16384 d
|
|
|
|
sendPacket ctx $ AppData sending
|
|
|
|
sendDataChunk remain
|
|
|
|
| otherwise = sendPacket ctx $ AppData d
|
2011-03-02 07:41:59 +00:00
|
|
|
|
2012-03-10 21:04:44 +00:00
|
|
|
-- | recvData get data out of Data packet, and automatically renegotiate if
|
2011-03-02 08:43:05 +00:00
|
|
|
-- a Handshake ClientHello is received
|
2012-03-15 07:53:03 +00:00
|
|
|
recvData :: MonadIO m => Context -> m B.ByteString
|
2011-03-02 07:41:59 +00:00
|
|
|
recvData ctx = do
|
2012-03-27 07:57:51 +00:00
|
|
|
checkValid ctx
|
|
|
|
pkt <- recvPacket ctx
|
|
|
|
case pkt of
|
|
|
|
-- on server context receiving a client hello == renegotiation
|
2012-11-16 15:37:05 +00:00
|
|
|
Right (Handshake [ch@(ClientHello _ _ _ _ _ _ (Just _))]) ->
|
|
|
|
-- reject renegotiation with SSLv2 header
|
|
|
|
case roleParams $ ctxParams ctx of
|
|
|
|
Server sparams -> error "assert, deprecated hello request in server context"
|
|
|
|
Client {} -> error "assert, unexpected client hello in client context"
|
2012-03-27 07:57:51 +00:00
|
|
|
Right (Handshake [ch@(ClientHello {})]) ->
|
2012-07-12 08:02:10 +00:00
|
|
|
case roleParams $ ctxParams ctx of
|
|
|
|
Server sparams -> handshakeServerWith sparams ctx ch >> recvData ctx
|
2012-10-20 08:00:55 +00:00
|
|
|
Client {} -> error "assert, unexpected client hello in client context"
|
2012-03-27 07:57:51 +00:00
|
|
|
-- on client context, receiving a hello request == renegotiation
|
|
|
|
Right (Handshake [HelloRequest]) ->
|
2012-07-12 08:02:10 +00:00
|
|
|
case roleParams $ ctxParams ctx of
|
2012-10-20 08:00:55 +00:00
|
|
|
Server {} -> error "assert, unexpected hello request in server context"
|
2012-07-12 08:02:10 +00:00
|
|
|
Client cparams -> handshakeClient cparams ctx >> recvData ctx
|
2012-03-27 07:57:51 +00:00
|
|
|
Right (Alert [(AlertLevel_Fatal, _)]) -> do
|
|
|
|
setEOF ctx
|
|
|
|
return B.empty
|
|
|
|
Right (Alert [(AlertLevel_Warning, CloseNotify)]) -> do
|
|
|
|
setEOF ctx
|
|
|
|
return B.empty
|
2012-10-29 21:23:44 +00:00
|
|
|
Right (AppData "") -> recvData ctx
|
|
|
|
Right (AppData x) -> return x
|
|
|
|
Right p -> error ("error unexpected packet: " ++ show p)
|
|
|
|
Left err -> error ("error received: " ++ show err)
|
2012-02-07 20:41:28 +00:00
|
|
|
|
2012-10-21 19:35:32 +00:00
|
|
|
{-# DEPRECATED recvData' "use recvData that returns strict bytestring" #-}
|
|
|
|
-- | same as recvData but returns a lazy bytestring.
|
2012-03-15 07:53:03 +00:00
|
|
|
recvData' :: MonadIO m => Context -> m L.ByteString
|
2012-02-07 20:41:28 +00:00
|
|
|
recvData' ctx = recvData ctx >>= return . L.fromChunks . (:[])
|