-- | TLS record layer in Tx direction
module Network.TLS.Record.Writing (
    encodeRecord,
    encodeRecord13,
    sendBytes,
) where

import Network.TLS.Cipher
import Network.TLS.Context.Internal
import Network.TLS.Hooks
import Network.TLS.Imports
import Network.TLS.Packet
import Network.TLS.Record
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 = Context -> RecordM ByteString -> IO (Either TLSError ByteString)
forall a. Context -> RecordM a -> IO (Either TLSError a)
prepareRecord Context
ctx (RecordM ByteString -> IO (Either TLSError ByteString))
-> (Record Plaintext -> RecordM ByteString)
-> Record Plaintext
-> IO (Either TLSError ByteString)
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
    txState <- MVar RecordState -> IO RecordState
forall a. MVar a -> IO a
readMVar (MVar RecordState -> IO RecordState)
-> MVar RecordState -> IO RecordState
forall a b. (a -> b) -> a -> b
$ Context -> MVar RecordState
ctxTxRecordState Context
ctx
    let sz = case RecordState -> Maybe Cipher
stCipher RecordState
txState of
            Maybe Cipher
Nothing -> Int
0
            Just Cipher
cipher ->
                if BulkFunctions -> Bool
hasRecordIV (BulkFunctions -> Bool) -> BulkFunctions -> Bool
forall a b. (a -> b) -> a -> b
$ Bulk -> BulkFunctions
bulkF (Bulk -> BulkFunctions) -> Bulk -> BulkFunctions
forall a b. (a -> b) -> a -> b
$ Cipher -> Bulk
cipherBulk Cipher
cipher
                    then Bulk -> Int
bulkIVSize (Bulk -> Int) -> Bulk -> Int
forall a b. (a -> b) -> a -> b
$ Cipher -> Bulk
cipherBulk Cipher
cipher
                    else Int
0 -- to not generate IV
    if sz > 0
        then do
            newIV <- getStateRNG ctx sz
            runTxRecordState ctx (modify (setRecordIV newIV) >> f)
        else runTxRecordState ctx f

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

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

encodeRecord13 :: Context -> Record Plaintext -> IO (Either TLSError ByteString)
encodeRecord13 :: Context -> Record Plaintext -> IO (Either TLSError ByteString)
encodeRecord13 Context
ctx = Context -> RecordM ByteString -> IO (Either TLSError ByteString)
forall a. Context -> RecordM a -> IO (Either TLSError a)
prepareRecord13 Context
ctx (RecordM ByteString -> IO (Either TLSError ByteString))
-> (Record Plaintext -> RecordM ByteString)
-> Record Plaintext
-> IO (Either TLSError ByteString)
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 = Context -> RecordM a -> IO (Either TLSError a)
forall a. Context -> RecordM a -> IO (Either TLSError a)
runTxRecordState

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

sendBytes :: Context -> ByteString -> IO ()
sendBytes :: Context -> ByteString -> IO ()
sendBytes Context
ctx ByteString
dataToSend = do
    Context -> (Logging -> IO ()) -> IO ()
withLog Context
ctx ((Logging -> IO ()) -> IO ()) -> (Logging -> IO ()) -> IO ()
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