-- |
-- Module      : Network.TLS.Record.Writing
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : unknown
--
-- TLS record layer in Tx direction
--
module Network.TLS.Record.Writing
    ( encodeRecord
    , encodeRecord13
    , sendBytes
    ) where

import Network.TLS.Cap
import Network.TLS.Cipher
import Network.TLS.Context.Internal
import Network.TLS.Hooks
import Network.TLS.Imports
import Network.TLS.Packet
import Network.TLS.Parameters
import Network.TLS.Record
import Network.TLS.State
import Network.TLS.Struct

import Control.Concurrent.MVar
import Control.Monad.State.Strict
import qualified Data.ByteString as B

encodeRecord :: Context -> Record Plaintext -> IO (Either TLSError ByteString)
encodeRecord :: Context -> Record Plaintext -> IO (Either TLSError ByteString)
encodeRecord Context
ctx = forall a. Context -> RecordM a -> IO (Either TLSError a)
prepareRecord Context
ctx forall b c a. (b -> c) -> (a -> b) -> a -> c
. Record Plaintext -> RecordM ByteString
encodeRecordM

-- before TLS 1.1, the block cipher IV is made of the residual of the previous block,
-- so we use cstIV as is, however in other case we generate an explicit IV
prepareRecord :: Context -> RecordM a -> IO (Either TLSError a)
prepareRecord :: forall a. Context -> RecordM a -> IO (Either TLSError a)
prepareRecord Context
ctx RecordM a
f = do
    Version
ver     <- forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx (Version -> TLSSt Version
getVersionWithDefault forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum forall a b. (a -> b) -> a -> b
$ Supported -> [Version]
supportedVersions forall a b. (a -> b) -> a -> b
$ Context -> Supported
ctxSupported Context
ctx)
    RecordState
txState <- forall a. MVar a -> IO a
readMVar forall a b. (a -> b) -> a -> b
$ Context -> MVar RecordState
ctxTxState Context
ctx
    let sz :: Int
sz = case RecordState -> Maybe Cipher
stCipher RecordState
txState of
                  Maybe Cipher
Nothing     -> Int
0
                  Just Cipher
cipher -> if BulkFunctions -> Bool
hasRecordIV forall a b. (a -> b) -> a -> b
$ Bulk -> BulkFunctions
bulkF forall a b. (a -> b) -> a -> b
$ Cipher -> Bulk
cipherBulk Cipher
cipher
                                    then Bulk -> Int
bulkIVSize forall a b. (a -> b) -> a -> b
$ Cipher -> Bulk
cipherBulk Cipher
cipher
                                    else Int
0 -- to not generate IV
    if Version -> Bool
hasExplicitBlockIV Version
ver Bool -> Bool -> Bool
&& Int
sz forall a. Ord a => a -> a -> Bool
> Int
0
        then do ByteString
newIV <- Context -> Int -> IO ByteString
getStateRNG Context
ctx Int
sz
                forall a. Context -> RecordM a -> IO (Either TLSError a)
runTxState Context
ctx (forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (ByteString -> RecordState -> RecordState
setRecordIV ByteString
newIV) forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> RecordM a
f)
        else forall a. Context -> RecordM a -> IO (Either TLSError a)
runTxState Context
ctx RecordM a
f

encodeRecordM :: Record Plaintext -> RecordM ByteString
encodeRecordM :: Record Plaintext -> RecordM ByteString
encodeRecordM Record Plaintext
record = do
    Record Ciphertext
erecord <- Record Plaintext -> RecordM (Record Ciphertext)
engageRecord Record Plaintext
record
    let (Header
hdr, ByteString
content) = forall a. Record a -> (Header, ByteString)
recordToRaw Record Ciphertext
erecord
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [ByteString] -> ByteString
B.concat [ Header -> ByteString
encodeHeader Header
hdr, ByteString
content ]

----------------------------------------------------------------

encodeRecord13 :: Context -> Record Plaintext -> IO (Either TLSError ByteString)
encodeRecord13 :: Context -> Record Plaintext -> IO (Either TLSError ByteString)
encodeRecord13 Context
ctx = forall a. Context -> RecordM a -> IO (Either TLSError a)
prepareRecord13 Context
ctx forall b c a. (b -> c) -> (a -> b) -> a -> c
. Record Plaintext -> RecordM ByteString
encodeRecordM

prepareRecord13 :: Context -> RecordM a -> IO (Either TLSError a)
prepareRecord13 :: forall a. Context -> RecordM a -> IO (Either TLSError a)
prepareRecord13 = forall a. Context -> RecordM a -> IO (Either TLSError a)
runTxState

----------------------------------------------------------------

sendBytes :: Context -> ByteString -> IO ()
sendBytes :: Context -> ByteString -> IO ()
sendBytes Context
ctx ByteString
dataToSend = do
    Context -> (Logging -> IO ()) -> IO ()
withLog Context
ctx forall a b. (a -> b) -> a -> b
$ \Logging
logging -> Logging -> ByteString -> IO ()
loggingIOSent Logging
logging ByteString
dataToSend
    Context -> ByteString -> IO ()
contextSend Context
ctx ByteString
dataToSend