File: Recv.hs

package info (click to toggle)
haskell-tls 2.1.8-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,056 kB
  • sloc: haskell: 15,695; makefile: 3
file content (112 lines) | stat: -rw-r--r-- 4,206 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
-- | TLS record layer in Rx direction
module Network.TLS.Record.Recv (
    recvRecord12,
    recvRecord13,
) where

import qualified Data.ByteString as B

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 Network.TLS.Types

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

getMyPlainLimit :: Context -> IO Int
getMyPlainLimit ctx = do
    msiz <- getMyRecordLimit ctx
    return $ case msiz of
        Nothing -> defaultRecordSizeLimit
        Just siz -> siz

getRecord
    :: Context
    -> Header
    -> ByteString
    -> IO (Either TLSError (Record Plaintext))
getRecord ctx header content = do
    withLog ctx $ \logging -> loggingIORecv logging header content
    lim <- getMyPlainLimit ctx
    runRxRecordState ctx $ do
        let erecord = rawToRecord header $ fragmentCiphertext content
        decryptRecord erecord lim

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

exceedsTLSCiphertext :: Int -> Word16 -> Bool
exceedsTLSCiphertext overhead actual =
    -- In TLS 1.3, overhead is included one more byte for content type.
    fromIntegral actual > defaultRecordSizeLimit + overhead

-- | recvRecord receive a full TLS record (header + data), from the other side.
--
-- The record is disengaged from the record layer
recvRecord12
    :: Context
    -- ^ TLS context
    -> IO (Either TLSError (Record Plaintext))
recvRecord12 ctx =
    readExactBytes ctx 5 >>= either (return . Left) (recvLengthE . decodeHeader)
  where
    recvLengthE = either (return . Left) recvLength

    recvLength header@(Header _ _ readlen) = do
        -- RFC 5246 Section 7.2.2
        -- A TLSCiphertext record was received that had a length more
        -- than 2^14+2048 bytes, or a record decrypted to a
        -- TLSCompressed record with more than 2^14+1024 bytes.  This
        -- message is always fatal and should never be observed in
        -- communication between proper implementations (except when
        -- messages were corrupted in the network).
        if exceedsTLSCiphertext 2048 readlen
            then return $ Left maximumSizeExceeded
            else
                readExactBytes ctx (fromIntegral readlen)
                    >>= either (return . Left) (getRecord ctx header)

recvRecord13 :: Context -> IO (Either TLSError (Record Plaintext))
recvRecord13 ctx = readExactBytes ctx 5 >>= either (return . Left) (recvLengthE . decodeHeader)
  where
    recvLengthE = either (return . Left) recvLength
    recvLength header@(Header _ _ readlen) = do
        -- RFC 8446 Section 5.2:
        -- An AEAD algorithm used in TLS 1.3 MUST NOT produce an
        -- expansion greater than 255 octets.  An endpoint that
        -- receives a record from its peer with TLSCiphertext.length
        -- larger than 2^14 + 256 octets MUST terminate the connection
        -- with a "record_overflow" alert.  This limit is derived from
        -- the maximum TLSInnerPlaintext length of 2^14 octets + 1
        -- octet for ContentType + the maximum AEAD expansion of 255
        -- octets.
        if exceedsTLSCiphertext 256 readlen
            then return $ Left maximumSizeExceeded
            else
                readExactBytes ctx (fromIntegral readlen)
                    >>= either (return . Left) (getRecord ctx header)

maximumSizeExceeded :: TLSError
maximumSizeExceeded = Error_Protocol "record exceeding maximum size" RecordOverflow

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

readExactBytes :: Context -> Int -> IO (Either TLSError ByteString)
readExactBytes ctx sz = do
    hdrbs <- contextRecv ctx sz
    if B.length hdrbs == sz
        then return $ Right hdrbs
        else do
            setEOF ctx
            return . Left $
                if B.null hdrbs
                    then Error_EOF
                    else
                        Error_Packet
                            ( "partial packet: expecting "
                                ++ show sz
                                ++ " bytes, got: "
                                ++ show (B.length hdrbs)
                            )