quic 0.1.8 → 0.1.9
raw patch · 97 files changed
+5505/−4531 lines, 97 filesdep +network-controldep −psqueuessetup-changed
Dependencies added: network-control
Dependencies removed: psqueues
Files
- ChangeLog.md +6/−0
- Network/QUIC.hs +69/−41
- Network/QUIC/Client.hs +27/−24
- Network/QUIC/Client/Reader.hs +38/−43
- Network/QUIC/Client/Run.hs +59/−40
- Network/QUIC/Common.hs +2/−2
- Network/QUIC/Config.hs +119/−93
- Network/QUIC/Connection.hs +12/−12
- Network/QUIC/Connection/Crypto.hs +142/−115
- Network/QUIC/Connection/Migration.hs +97/−86
- Network/QUIC/Connection/Misc.hs +41/−40
- Network/QUIC/Connection/PacketNumber.hs +4/−4
- Network/QUIC/Connection/Role.hs +60/−52
- Network/QUIC/Connection/State.hs +41/−73
- Network/QUIC/Connection/Stream.hs +43/−36
- Network/QUIC/Connection/StreamTable.hs +35/−32
- Network/QUIC/Connection/Timeout.hs +6/−6
- Network/QUIC/Connection/Types.hs +251/−212
- Network/QUIC/Connector.hs +29/−27
- Network/QUIC/Crypto.hs +6/−6
- Network/QUIC/Crypto/Fusion.hs +11/−11
- Network/QUIC/Crypto/Keys.hs +39/−36
- Network/QUIC/Crypto/Nite.hs +129/−100
- Network/QUIC/Crypto/Types.hs +26/−26
- Network/QUIC/Crypto/Utils.hs +25/−22
- Network/QUIC/Event.hs +9/−8
- Network/QUIC/Exception.hs +8/−8
- Network/QUIC/IO.hs +71/−69
- Network/QUIC/Info.hs +61/−42
- Network/QUIC/Internal.hs +17/−17
- Network/QUIC/Logger.hs +8/−8
- Network/QUIC/Packet.hs +39/−35
- Network/QUIC/Packet/Decode.hs +44/−43
- Network/QUIC/Packet/Decrypt.hs +41/−37
- Network/QUIC/Packet/Encode.hs +115/−47
- Network/QUIC/Packet/Frame.hs +109/−104
- Network/QUIC/Packet/Header.hs +42/−26
- Network/QUIC/Packet/Number.hs +7/−3
- Network/QUIC/Packet/Token.hs +27/−27
- Network/QUIC/Parameters.hs +194/−169
- Network/QUIC/QLogger.hs +5/−4
- Network/QUIC/Qlog.hs +198/−105
- Network/QUIC/Receiver.hs +212/−178
- Network/QUIC/Recovery.hs +47/−47
- Network/QUIC/Recovery/Constants.hs +1/−2
- Network/QUIC/Recovery/Detect.hs +39/−29
- Network/QUIC/Recovery/Interface.hs +6/−6
- Network/QUIC/Recovery/LossRecovery.hs +106/−89
- Network/QUIC/Recovery/Metrics.hs +66/−52
- Network/QUIC/Recovery/Misc.hs +9/−9
- Network/QUIC/Recovery/PeerPacketNumbers.hs +10/−10
- Network/QUIC/Recovery/Persistent.hs +36/−38
- Network/QUIC/Recovery/Release.hs +30/−26
- Network/QUIC/Recovery/Timer.hs +114/−111
- Network/QUIC/Recovery/Types.hs +261/−215
- Network/QUIC/Recovery/Utils.hs +25/−24
- Network/QUIC/Sender.hs +222/−198
- Network/QUIC/Server.hs +23/−21
- Network/QUIC/Server/Reader.hs +9/−13
- Network/QUIC/Stream.hs +42/−46
- Network/QUIC/Stream/Frag.hs +22/−14
- Network/QUIC/Stream/Misc.hs +32/−40
- Network/QUIC/Stream/Reass.hs +107/−97
- Network/QUIC/Stream/Skew.hs +27/−25
- Network/QUIC/Stream/Table.hs +26/−25
- Network/QUIC/Stream/Types.hs +56/−66
- Network/QUIC/TLS.hs +86/−73
- Network/QUIC/Types.hs +16/−16
- Network/QUIC/Types/Ack.hs +28/−29
- Network/QUIC/Types/CID.hs +21/−20
- Network/QUIC/Types/Error.hs +4/−1
- Network/QUIC/Types/Exception.hs +28/−26
- Network/QUIC/Types/Frame.hs +33/−25
- Network/QUIC/Types/Integer.hs +22/−21
- Network/QUIC/Types/Packet.hs +79/−60
- Network/QUIC/Types/Resumption.hs +18/−16
- Network/QUIC/Types/Time.hs +17/−17
- Network/QUIC/Utils.hs +3/−3
- Network/QUIC/Windows.hs +4/−4
- Setup.hs +1/−0
- quic.cabal +2/−2
- test/Config.hs +116/−103
- test/ErrorSpec.hs +7/−4
- test/FrameSpec.hs +1/−1
- test/HandshakeSpec.hs +57/−34
- test/IOSpec.hs +11/−6
- test/PacketSpec.hs +137/−103
- test/RecoverySpec.hs +87/−32
- test/TLSSpec.hs +78/−61
- test/TransportError.hs +276/−179
- test/TypesSpec.hs +3/−1
- util/ClientX.hs +24/−22
- util/Common.hs +19/−19
- util/H3.hs +21/−17
- util/ServerX.hs +32/−30
- util/client.hs +349/−267
- util/server.hs +85/−67
ChangeLog.md view
@@ -1,3 +1,9 @@+## 0.1.9++- Using the network-control package.+- Rate control for some frames.+- Announcing MaxStreams correctly.+ ## 0.1.8 - Announcing MaxStreams properly.
Network/QUIC.hs view
@@ -4,47 +4,75 @@ -- -- The -threaded option must be specified to GHC to use this library. module Network.QUIC (- -- * Connection- Connection- , abortConnection- -- * Stream- , Stream- , StreamId- , streamId- -- ** Category- , isClientInitiatedBidirectional- , isServerInitiatedBidirectional- , isClientInitiatedUnidirectional- , isServerInitiatedUnidirectional- -- ** Opening- , stream- , unidirectionalStream- , acceptStream- -- ** Closing- , closeStream- , shutdownStream- , resetStream- , stopStream- -- * IO- , recvStream- , sendStream- , sendStreamMany- -- * Information- , ConnectionInfo(..)- , getConnectionInfo- -- * Statistics- , ConnectionStats(..)- , getConnectionStats- -- * Synchronization- , wait0RTTReady- , wait1RTTReady- , waitEstablished- -- * Exceptions and Errors- , QUICException(..)- , TransportError(.., NoError, InternalError, ConnectionRefused, FlowControlError, StreamLimitError, StreamStateError, FinalSizeError, FrameEncodingError, TransportParameterError, ConnectionIdLimitError, ProtocolViolation, InvalidToken, ApplicationError, CryptoBufferExceeded, KeyUpdateError, AeadLimitReached, NoViablePath)- , cryptoError- , ApplicationProtocolError(..)- ) where+ -- * Connection+ Connection,+ abortConnection,++ -- * Stream+ Stream,+ StreamId,+ streamId,++ -- ** Category+ isClientInitiatedBidirectional,+ isServerInitiatedBidirectional,+ isClientInitiatedUnidirectional,+ isServerInitiatedUnidirectional,++ -- ** Opening+ stream,+ unidirectionalStream,+ acceptStream,++ -- ** Closing+ closeStream,+ shutdownStream,+ resetStream,+ stopStream,++ -- * IO+ recvStream,+ sendStream,+ sendStreamMany,++ -- * Information+ ConnectionInfo (..),+ getConnectionInfo,++ -- * Statistics+ ConnectionStats (..),+ getConnectionStats,++ -- * Synchronization+ wait0RTTReady,+ wait1RTTReady,+ waitEstablished,++ -- * Exceptions and Errors+ QUICException (..),+ TransportError (+ ..,+ NoError,+ InternalError,+ ConnectionRefused,+ FlowControlError,+ StreamLimitError,+ StreamStateError,+ FinalSizeError,+ FrameEncodingError,+ TransportParameterError,+ ConnectionIdLimitError,+ ProtocolViolation,+ InvalidToken,+ ApplicationError,+ CryptoBufferExceeded,+ KeyUpdateError,+ AeadLimitReached,+ NoViablePath+ ),+ cryptoError,+ ApplicationProtocolError (..),+) where import Network.QUIC.Connection import Network.QUIC.IO
Network/QUIC/Client.hs view
@@ -2,30 +2,33 @@ -- When a new better network interface is up, -- migration is done automatically. module Network.QUIC.Client (- -- * Running a QUIC client- run- -- * Configration- , ClientConfig- , defaultClientConfig- , ccServerName- , ccPortName- , ccALPN- , ccUse0RTT- , ccResumption- , ccCiphers- , ccGroups- , ccVersions--- , ccCredentials- , ccValidate- , ccAutoMigration- -- * Resumption- , ResumptionInfo- , getResumptionInfo- , isResumptionPossible- , is0RTTPossible- -- * Migration- , migrate- ) where+ -- * Running a QUIC client+ run,++ -- * Configration+ ClientConfig,+ defaultClientConfig,+ ccServerName,+ ccPortName,+ ccALPN,+ ccUse0RTT,+ ccResumption,+ ccCiphers,+ ccGroups,+ ccVersions,+ -- , ccCredentials+ ccValidate,+ ccAutoMigration,++ -- * Resumption+ ResumptionInfo,+ getResumptionInfo,+ isResumptionPossible,+ is0RTTPossible,++ -- * Migration+ migrate,+) where import Network.QUIC.Client.Run import Network.QUIC.Config
Network/QUIC/Client/Reader.hs view
@@ -1,13 +1,12 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module Network.QUIC.Client.Reader (- readerClient- , recvClient- , ConnectionControl(..)- , controlConnection- ) where+ readerClient,+ recvClient,+ ConnectionControl (..),+ controlConnection,+) where import Data.List (intersect) import Network.Socket (getSocketName)@@ -25,9 +24,6 @@ import Network.QUIC.Qlog import Network.QUIC.Recovery import Network.QUIC.Types-#if defined(mingw32_HOST_OS)-import Network.QUIC.Windows-#endif -- | readerClient dies when the socket is closed. readerClient :: UDPSocket -> Connection -> IO ()@@ -44,32 +40,30 @@ wait loop = do ito <- readMinIdleTimeout conn- mbs <- timeout ito "readeClient" $-#if defined(mingw32_HOST_OS)- windowsThreadBlockHack $-#endif- recv cs0+ mbs <-+ timeout ito "readeClient" $+ recv cs0 case mbs of- Nothing -> close cs0- Just bs -> do- now <- getTimeMicrosecond- pkts <- decodePackets bs- mapM_ (putQ now) pkts- loop+ Nothing -> close cs0+ Just bs -> do+ now <- getTimeMicrosecond+ pkts <- decodePackets bs+ mapM_ (putQ now) pkts+ loop logAction msg = connDebugLog conn ("debug: readerClient: " <> msg) putQ _ (PacketIB BrokenPacket) = return () putQ t (PacketIV pkt@(VersionNegotiationPacket dCID sCID peerVers)) = do qlogReceived conn pkt t myVerInfo <- getVersionInfo conn- let myVer = chosenVersion myVerInfo+ let myVer = chosenVersion myVerInfo myVers0 = otherVersions myVerInfo -- ignoring VN if the original version is included. when (myVer `notElem` peerVers && Negotiation `notElem` peerVers) $ do ok <- checkCIDs conn dCID (Left sCID) let myVers = filter (not . isGreasingVersion) myVers0 nextVerInfo = case myVers `intersect` peerVers of- vers@(ver:_) | ok -> VersionInfo ver vers- _ -> brokenVersionInfo+ vers@(ver : _) | ok -> VersionInfo ver vers+ _ -> brokenVersionInfo E.throwTo (mainThreadId conn) $ VerNego nextVerInfo putQ t (PacketIC pkt lvl siz) = writeRecvQ (connRecvQ conn) $ mkReceivedPacket pkt t siz lvl putQ t (PacketIR pkt@(RetryPacket ver dCID sCID token ex)) = do@@ -77,7 +71,7 @@ ok <- checkCIDs conn dCID ex when ok $ do resetPeerCID conn sCID- setPeerAuthCIDs conn $ \auth -> auth { retrySrcCID = Just sCID }+ setPeerAuthCIDs conn $ \auth -> auth{retrySrcCID = Just sCID} initializeCoder conn InitialLevel $ initialSecrets ver sCID setToken conn token setRetried conn True@@ -85,12 +79,12 @@ where put ppkt = putOutput conn $ OutRetrans ppkt -checkCIDs :: Connection -> CID -> Either CID (ByteString,ByteString) -> IO Bool+checkCIDs :: Connection -> CID -> Either CID (ByteString, ByteString) -> IO Bool checkCIDs conn dCID (Left sCID) = do localCID <- getMyCID conn remoteCID <- getPeerCID conn return (dCID == localCID && sCID == remoteCID)-checkCIDs conn dCID (Right (pseudo0,tag)) = do+checkCIDs conn dCID (Right (pseudo0, tag)) = do localCID <- getMyCID conn remoteCID <- getPeerCID conn ver <- getVersion conn@@ -103,30 +97,31 @@ ---------------------------------------------------------------- -- | How to control a connection.-data ConnectionControl = ChangeServerCID- | ChangeClientCID- | NATRebinding- | ActiveMigration- deriving (Eq, Show)+data ConnectionControl+ = ChangeServerCID+ | ChangeClientCID+ | NATRebinding+ | ActiveMigration+ deriving (Eq, Show) controlConnection :: Connection -> ConnectionControl -> IO Bool controlConnection conn typ- | isClient conn = do+ | isClient conn = do waitEstablished conn controlConnection' conn typ- | otherwise = return False+ | otherwise = return False controlConnection' :: Connection -> ConnectionControl -> IO Bool controlConnection' conn ChangeServerCID = do mn <- timeout (Microseconds 1000000) "controlConnection' 1" $ waitPeerCID conn -- fixme case mn of- Nothing -> return False- Just (CIDInfo n _ _) -> do- sendFrames conn RTT1Level [RetireConnectionID n]- return True+ Nothing -> return False+ Just (CIDInfo n _ _) -> do+ sendFrames conn RTT1Level [RetireConnectionID n]+ return True controlConnection' conn ChangeClientCID = do cidInfo <- getNewMyCID conn- x <- (+1) <$> getMyCIDSeqNum conn+ x <- (+ 1) <$> getMyCIDSeqNum conn sendFrames conn RTT1Level [NewConnectionID cidInfo x] return True controlConnection' conn NATRebinding = do@@ -135,11 +130,11 @@ controlConnection' conn ActiveMigration = do mn <- timeout (Microseconds 1000000) "controlConnection' 2" $ waitPeerCID conn -- fixme case mn of- Nothing -> return False- mcidinfo -> do- rebind conn $ Microseconds 5000000- validatePath conn mcidinfo- return True+ Nothing -> return False+ mcidinfo -> do+ rebind conn $ Microseconds 5000000+ validatePath conn mcidinfo+ return True rebind :: Connection -> Microseconds -> IO () rebind conn microseconds = do
Network/QUIC/Client/Run.hs view
@@ -3,12 +3,12 @@ {-# LANGUAGE ScopedTypeVariables #-} module Network.QUIC.Client.Run (- run- , migrate- ) where+ run,+ migrate,+) where import qualified Network.Socket as NS-import Network.UDP (UDPSocket(..))+import Network.UDP (UDPSocket (..)) import qualified Network.UDP as UDP import UnliftIO.Async import UnliftIO.Concurrent@@ -41,50 +41,55 @@ run :: ClientConfig -> (Connection -> IO a) -> IO a -- Don't use handleLogUnit here because of a return value. run conf client = NS.withSocketsDo $ do- let resInfo = ccResumption conf- verInfo = case resumptionSession resInfo of- Nothing | resumptionToken resInfo == emptyToken ->- let vers = ccVersions conf- ver = head vers- in VersionInfo ver vers- _ -> let ver = resumptionVersion resInfo in VersionInfo ver [ver]- ex <- E.try $ runClient conf client False verInfo- case ex of- Right v -> return v- Left (NextVersion nextVerInfo)- | verInfo == brokenVersionInfo -> E.throwIO VersionNegotiationFailed- | otherwise -> runClient conf client True nextVerInfo+ let resInfo = ccResumption conf+ verInfo = case resumptionSession resInfo of+ Nothing+ | resumptionToken resInfo == emptyToken ->+ let vers = ccVersions conf+ ver = head vers+ in VersionInfo ver vers+ _ -> let ver = resumptionVersion resInfo in VersionInfo ver [ver]+ ex <- E.try $ runClient conf client False verInfo+ case ex of+ Right v -> return v+ Left (NextVersion nextVerInfo)+ | verInfo == brokenVersionInfo -> E.throwIO VersionNegotiationFailed+ | otherwise -> runClient conf client True nextVerInfo runClient :: ClientConfig -> (Connection -> IO a) -> Bool -> VersionInfo -> IO a runClient conf client0 isICVN verInfo = do E.bracket open clse $ \(ConnRes conn myAuthCIDs reader) -> do forkIO reader >>= addReader conn- let conf' = conf {- ccParameters = (ccParameters conf) {- versionInformation = Just verInfo+ let conf' =+ conf+ { ccParameters =+ (ccParameters conf)+ { versionInformation = Just verInfo+ } }- } setIncompatibleVN conn isICVN -- must be before handshaker setToken conn $ resumptionToken $ ccResumption conf handshaker <- handshakeClient conf' conn myAuthCIDs let client = do- if ccUse0RTT conf then- wait0RTTReady conn- else- wait1RTTReady conn+ if ccUse0RTT conf+ then wait0RTTReady conn+ else wait1RTTReady conn client0 conn ldcc = connLDCC conn- supporters = foldr1 concurrently_ [handshaker- ,sender conn- ,receiver conn- ,resender ldcc- ,ldccTimer ldcc- ]+ supporters =+ foldr1+ concurrently_+ [ handshaker+ , sender conn+ , receiver conn+ , resender ldcc+ , ldccTimer ldcc+ ] runThreads = do er <- race supporters client case er of- Left () -> E.throwIO MustNotReached- Right r -> return r+ Left () -> E.throwIO MustNotReached+ Right r -> return r ex <- E.trySyncOrAsync runThreads sendFinal conn closure conn ldcc ex@@ -98,23 +103,37 @@ createClientConnection :: ClientConfig -> VersionInfo -> IO ConnRes createClientConnection conf@ClientConfig{..} verInfo = do- us@(UDPSocket _ sa _) <- UDP.clientSocket ccServerName ccPortName (not ccAutoMigration)+ us@(UDPSocket _ sa _) <-+ UDP.clientSocket ccServerName ccPortName (not ccAutoMigration) q <- newRecvQ sref <- newIORef us let send = \buf siz -> do cs <- readIORef sref UDP.sendBuf cs buf siz recv = recvClient q- myCID <- newCID+ myCID <- newCID peerCID <- newCID now <- getTimeMicrosecond (qLog, qclean) <- dirQLogger ccQLog now peerCID "client"- let debugLog msg | ccDebugLog = stdoutLogger msg- | otherwise = return ()+ let debugLog msg+ | ccDebugLog = stdoutLogger msg+ | otherwise = return () debugLog $ "Original CID: " <> bhow peerCID- let myAuthCIDs = defaultAuthCIDs { initSrcCID = Just myCID }- peerAuthCIDs = defaultAuthCIDs { initSrcCID = Just peerCID, origDstCID = Just peerCID }- conn <- clientConnection conf verInfo myAuthCIDs peerAuthCIDs debugLog qLog ccHooks sref q send recv+ let myAuthCIDs = defaultAuthCIDs{initSrcCID = Just myCID}+ peerAuthCIDs = defaultAuthCIDs{initSrcCID = Just peerCID, origDstCID = Just peerCID}+ conn <-+ clientConnection+ conf+ verInfo+ myAuthCIDs+ peerAuthCIDs+ debugLog+ qLog+ ccHooks+ sref+ q+ send+ recv addResource conn qclean let ver = chosenVersion verInfo initializeCoder conn InitialLevel $ initialSecrets ver peerCID
Network/QUIC/Common.hs view
@@ -17,8 +17,8 @@ defaultPacketSize :: NS.SockAddr -> Int defaultPacketSize NS.SockAddrInet6{} = defaultQUICPacketSizeForIPv6-defaultPacketSize _ = defaultQUICPacketSizeForIPv4+defaultPacketSize _ = defaultQUICPacketSizeForIPv4 maximumPacketSize :: NS.SockAddr -> Int maximumPacketSize NS.SockAddrInet6{} = 1500 - 40 - 8 -- fixme-maximumPacketSize _ = 1500 - 20 - 8 -- fixme+maximumPacketSize _ = 1500 - 20 - 8 -- fixme
Network/QUIC/Config.hs view
@@ -5,7 +5,7 @@ import Data.IP import Network.Socket-import Network.TLS hiding (Version, HostName, Hooks)+import Network.TLS hiding (Hooks, HostName, Version) import Network.TLS.QUIC import Network.QUIC.Imports@@ -16,112 +16,138 @@ ---------------------------------------------------------------- -- | Hooks.-data Hooks = Hooks {- onCloseCompleted :: IO ()- , onPlainCreated :: EncryptionLevel -> Plain -> Plain- , onTransportParametersCreated :: Parameters -> Parameters- , onTLSExtensionCreated :: [ExtensionRaw] -> [ExtensionRaw]- , onTLSHandshakeCreated :: [(EncryptionLevel,CryptoData)] -> ([(EncryptionLevel,CryptoData)],Bool)- , onResetStreamReceived :: Stream -> ApplicationProtocolError -> IO ()- , onServerReady :: IO ()- }+data Hooks = Hooks+ { onCloseCompleted :: IO ()+ , onPlainCreated :: EncryptionLevel -> Plain -> Plain+ , onTransportParametersCreated :: Parameters -> Parameters+ , onTLSExtensionCreated :: [ExtensionRaw] -> [ExtensionRaw]+ , onTLSHandshakeCreated+ :: [(EncryptionLevel, CryptoData)]+ -> ([(EncryptionLevel, CryptoData)], Bool)+ , onResetStreamReceived :: Stream -> ApplicationProtocolError -> IO ()+ , onServerReady :: IO ()+ } -- | Default hooks. defaultHooks :: Hooks-defaultHooks = Hooks {- onCloseCompleted = return ()- , onPlainCreated = \_l p -> p- , onTransportParametersCreated = id- , onTLSExtensionCreated = id- , onTLSHandshakeCreated = (, False)- , onResetStreamReceived = \_ _ -> return ()- , onServerReady = return ()- }+defaultHooks =+ Hooks+ { onCloseCompleted = return ()+ , onPlainCreated = \_l p -> p+ , onTransportParametersCreated = id+ , onTLSExtensionCreated = id+ , onTLSHandshakeCreated = (,False)+ , onResetStreamReceived = \_ _ -> return ()+ , onServerReady = return ()+ } ---------------------------------------------------------------- -- | Client configuration.-data ClientConfig = ClientConfig {- ccVersions :: [Version] -- ^ Compatible versions in the preferred order.- , ccCiphers :: [Cipher] -- ^ Cipher candidates defined in TLS 1.3.- , ccGroups :: [Group] -- ^ Key exchange group candidates defined in TLS 1.3.- , ccParameters :: Parameters- , ccKeyLog :: String -> IO ()- , ccQLog :: Maybe FilePath- , ccCredentials :: Credentials -- ^ TLS credentials.- , ccHooks :: Hooks- , ccUse0RTT :: Bool -- ^ Use 0-RTT on the 2nd connection if possible.- -- client original- , ccServerName :: HostName -- ^ Used to create a socket and SNI for TLS.- , ccPortName :: ServiceName -- ^ Used to create a socket.- , ccALPN :: Version -> IO (Maybe [ByteString]) -- ^ An ALPN provider.- , ccValidate :: Bool -- ^ Authenticating a server based on its certificate.- , ccResumption :: ResumptionInfo -- ^ Use resumption on the 2nd connection if possible.- , ccPacketSize :: Maybe Int -- ^ QUIC packet size (UDP payload size)- , ccDebugLog :: Bool- , ccAutoMigration :: Bool -- ^ If 'True', use a unconnected socket for auto migration. Otherwise, use a connected socket.- }+data ClientConfig = ClientConfig+ { ccVersions :: [Version]+ -- ^ Compatible versions in the preferred order.+ , ccCiphers :: [Cipher]+ -- ^ Cipher candidates defined in TLS 1.3.+ , ccGroups :: [Group]+ -- ^ Key exchange group candidates defined in TLS 1.3.+ , ccParameters :: Parameters+ , ccKeyLog :: String -> IO ()+ , ccQLog :: Maybe FilePath+ , ccCredentials :: Credentials+ -- ^ TLS credentials.+ , ccHooks :: Hooks+ , ccUse0RTT :: Bool+ -- ^ Use 0-RTT on the 2nd connection if possible.+ -- client original+ , ccServerName :: HostName+ -- ^ Used to create a socket and SNI for TLS.+ , ccPortName :: ServiceName+ -- ^ Used to create a socket.+ , ccALPN :: Version -> IO (Maybe [ByteString])+ -- ^ An ALPN provider.+ , ccValidate :: Bool+ -- ^ Authenticating a server based on its certificate.+ , ccResumption :: ResumptionInfo+ -- ^ Use resumption on the 2nd connection if possible.+ , ccPacketSize :: Maybe Int+ -- ^ QUIC packet size (UDP payload size)+ , ccDebugLog :: Bool+ , ccAutoMigration :: Bool+ -- ^ If 'True', use a unconnected socket for auto migration. Otherwise, use a connected socket.+ } -- | The default value for client configuration. defaultClientConfig :: ClientConfig-defaultClientConfig = ClientConfig {- ccVersions = [Version2,Version1]- , ccCiphers = supportedCiphers defaultSupported- , ccGroups = supportedGroups defaultSupported- , ccParameters = defaultParameters- , ccKeyLog = \_ -> return ()- , ccQLog = Nothing- , ccCredentials = mempty- , ccHooks = defaultHooks- , ccUse0RTT = False- -- client original- , ccServerName = "127.0.0.1"- , ccPortName = "4433"- , ccALPN = \_ -> return Nothing- , ccValidate = True- , ccResumption = defaultResumptionInfo- , ccPacketSize = Nothing- , ccDebugLog = False- , ccAutoMigration = True- }+defaultClientConfig =+ ClientConfig+ { ccVersions = [Version2, Version1]+ , ccCiphers = supportedCiphers defaultSupported+ , ccGroups = supportedGroups defaultSupported+ , ccParameters = defaultParameters+ , ccKeyLog = \_ -> return ()+ , ccQLog = Nothing+ , ccCredentials = mempty+ , ccHooks = defaultHooks+ , ccUse0RTT = False+ , -- client original+ ccServerName = "127.0.0.1"+ , ccPortName = "4433"+ , ccALPN = \_ -> return Nothing+ , ccValidate = True+ , ccResumption = defaultResumptionInfo+ , ccPacketSize = Nothing+ , ccDebugLog = False+ , ccAutoMigration = True+ } ---------------------------------------------------------------- -- | Server configuration.-data ServerConfig = ServerConfig {- scVersions :: [Version] -- ^ Fully-Deployed Versions in the preferred order.- , scCiphers :: [Cipher] -- ^ Cipher candidates defined in TLS 1.3.- , scGroups :: [Group] -- ^ Key exchange group candidates defined in TLS 1.3.- , scParameters :: Parameters- , scKeyLog :: String -> IO ()- , scQLog :: Maybe FilePath- , scCredentials :: Credentials -- ^ Server certificate information.- , scHooks :: Hooks- , scUse0RTT :: Bool -- ^ Use 0-RTT on the 2nd connection if possible.- -- server original- , scAddresses :: [(IP,PortNumber)] -- ^ Server addresses assigned to used network interfaces.- , scALPN :: Maybe (Version -> [ByteString] -> IO ByteString) -- ^ ALPN handler.- , scRequireRetry :: Bool -- ^ Requiring QUIC retry.- , scSessionManager :: SessionManager -- ^ A session manager of TLS 1.3.- , scDebugLog :: Maybe FilePath- }+data ServerConfig = ServerConfig+ { scVersions :: [Version]+ -- ^ Fully-Deployed Versions in the preferred order.+ , scCiphers :: [Cipher]+ -- ^ Cipher candidates defined in TLS 1.3.+ , scGroups :: [Group]+ -- ^ Key exchange group candidates defined in TLS 1.3.+ , scParameters :: Parameters+ , scKeyLog :: String -> IO ()+ , scQLog :: Maybe FilePath+ , scCredentials :: Credentials+ -- ^ Server certificate information.+ , scHooks :: Hooks+ , scUse0RTT :: Bool+ -- ^ Use 0-RTT on the 2nd connection if possible.+ -- server original+ , scAddresses :: [(IP, PortNumber)]+ -- ^ Server addresses assigned to used network interfaces.+ , scALPN :: Maybe (Version -> [ByteString] -> IO ByteString)+ -- ^ ALPN handler.+ , scRequireRetry :: Bool+ -- ^ Requiring QUIC retry.+ , scSessionManager :: SessionManager+ -- ^ A session manager of TLS 1.3.+ , scDebugLog :: Maybe FilePath+ } -- | The default value for server configuration. defaultServerConfig :: ServerConfig-defaultServerConfig = ServerConfig {- scVersions = [Version2,Version1]- , scCiphers = supportedCiphers defaultSupported- , scGroups = supportedGroups defaultSupported- , scParameters = defaultParameters- , scKeyLog = \_ -> return ()- , scQLog = Nothing- , scCredentials = mempty- , scHooks = defaultHooks- , scUse0RTT = False- -- server original- , scAddresses = [("0.0.0.0",4433),("::",4433)]- , scALPN = Nothing- , scRequireRetry = False- , scSessionManager = noSessionManager- , scDebugLog = Nothing- }+defaultServerConfig =+ ServerConfig+ { scVersions = [Version2, Version1]+ , scCiphers = supportedCiphers defaultSupported+ , scGroups = supportedGroups defaultSupported+ , scParameters = defaultParameters+ , scKeyLog = \_ -> return ()+ , scQLog = Nothing+ , scCredentials = mempty+ , scHooks = defaultHooks+ , scUse0RTT = False+ , -- server original+ scAddresses = [("0.0.0.0", 4433), ("::", 4433)]+ , scALPN = Nothing+ , scRequireRetry = False+ , scSessionManager = noSessionManager+ , scDebugLog = Nothing+ }
Network/QUIC/Connection.hs view
@@ -1,16 +1,16 @@ module Network.QUIC.Connection (- module Network.QUIC.Connection.PacketNumber- , module Network.QUIC.Connection.Crypto- , module Network.QUIC.Connection.Migration- , module Network.QUIC.Connection.Misc- , module Network.QUIC.Connection.State- , module Network.QUIC.Connection.Stream- , module Network.QUIC.Connection.StreamTable- , module Network.QUIC.Connection.Queue- , module Network.QUIC.Connection.Role- , module Network.QUIC.Connection.Timeout- , module Network.QUIC.Connection.Types- ) where+ module Network.QUIC.Connection.PacketNumber,+ module Network.QUIC.Connection.Crypto,+ module Network.QUIC.Connection.Migration,+ module Network.QUIC.Connection.Misc,+ module Network.QUIC.Connection.State,+ module Network.QUIC.Connection.Stream,+ module Network.QUIC.Connection.StreamTable,+ module Network.QUIC.Connection.Queue,+ module Network.QUIC.Connection.Role,+ module Network.QUIC.Connection.Timeout,+ module Network.QUIC.Connection.Types,+) where import Network.QUIC.Connection.Crypto import Network.QUIC.Connection.Migration
Network/QUIC/Connection/Crypto.hs view
@@ -1,28 +1,28 @@-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE RecordWildCards #-} module Network.QUIC.Connection.Crypto (- setEncryptionLevel- , waitEncryptionLevel- , putOffCrypto- --- , getCipher- , setCipher- , getTLSMode- , getApplicationProtocol- , setNegotiated- --- , dropSecrets- --- , initializeCoder- , initializeCoder1RTT- , updateCoder1RTT- , getCoder- , getProtector- --- , getCurrentKeyPhase- , setCurrentKeyPhase- ) where+ setEncryptionLevel,+ waitEncryptionLevel,+ putOffCrypto,+ --+ getCipher,+ setCipher,+ getTLSMode,+ getApplicationProtocol,+ setNegotiated,+ --+ dropSecrets,+ --+ initializeCoder,+ initializeCoder1RTT,+ updateCoder1RTT,+ getCoder,+ getProtector,+ --+ getCurrentKeyPhase,+ setCurrentKeyPhase,+) where import Network.TLS.QUIC import UnliftIO.STM@@ -49,12 +49,12 @@ atomically $ do writeTVar (encryptionLevel connState) lvl case lvl of- HandshakeLevel -> do- readTVar (pendingQ ! RTT0Level) >>= mapM_ (prependRecvQ q)- readTVar (pendingQ ! HandshakeLevel) >>= mapM_ (prependRecvQ q)- RTT1Level ->- readTVar (pendingQ ! RTT1Level) >>= mapM_ (prependRecvQ q)- _ -> return ()+ HandshakeLevel -> do+ readTVar (pendingQ ! RTT0Level) >>= mapM_ (prependRecvQ q)+ readTVar (pendingQ ! HandshakeLevel) >>= mapM_ (prependRecvQ q)+ RTT1Level ->+ readTVar (pendingQ ! RTT1Level) >>= mapM_ (prependRecvQ q)+ _ -> return () putOffCrypto :: Connection -> EncryptionLevel -> ReceivedPacket -> IO () putOffCrypto Connection{..} lvl rpkt =@@ -81,13 +81,20 @@ getApplicationProtocol :: Connection -> IO (Maybe NegotiatedProtocol) getApplicationProtocol Connection{..} = applicationProtocol <$> readIORef negotiated -setNegotiated :: Connection -> HandshakeMode13 -> Maybe NegotiatedProtocol -> ApplicationSecretInfo -> IO ()+setNegotiated+ :: Connection+ -> HandshakeMode13+ -> Maybe NegotiatedProtocol+ -> ApplicationSecretInfo+ -> IO () setNegotiated Connection{..} mode mproto appSecInf =- writeIORef negotiated Negotiated {- tlsHandshakeMode = mode- , applicationProtocol = mproto- , applicationSecretInfo = appSecInf- }+ writeIORef+ negotiated+ Negotiated+ { tlsHandshakeMode = mode+ , applicationProtocol = mproto+ , applicationSecretInfo = appSecInf+ } ---------------------------------------------------------------- @@ -100,17 +107,16 @@ initializeCoder :: Connection -> EncryptionLevel -> TrafficSecrets a -> IO () initializeCoder conn lvl sec = do- ver <- if lvl == RTT0Level then- return $ getOriginalVersion conn- else- getVersion conn+ ver <-+ if lvl == RTT0Level+ then return $ getOriginalVersion conn+ else getVersion conn cipher <- getCipher conn lvl avail <- isFusionAvailable (coder, protector) <-- if useFusion && avail then- genFusionCoder (isClient conn) ver cipher sec- else- genNiteCoder (isClient conn) ver cipher sec+ if useFusion && avail+ then genFusionCoder (isClient conn) ver cipher sec+ else genNiteCoder (isClient conn) ver cipher sec writeArray (coders conn) lvl coder writeArray (protectors conn) lvl protector @@ -120,10 +126,9 @@ cipher <- getCipher conn RTT1Level avail <- isFusionAvailable (coder, protector) <-- if useFusion && avail then- genFusionCoder (isClient conn) ver cipher sec- else- genNiteCoder (isClient conn) ver cipher sec+ if useFusion && avail+ then genFusionCoder (isClient conn) ver cipher sec+ else genNiteCoder (isClient conn) ver cipher sec let coder1 = Coder1RTT coder sec writeArray (coders1RTT conn) False coder1 writeArray (protectors conn) RTT1Level protector@@ -136,126 +141,148 @@ Coder1RTT coder secN <- readArray (coders1RTT conn) (not nextPhase) let secN1 = updateSecret ver cipher secN avail <- isFusionAvailable- coderN1 <- if useFusion && avail then- genFusionCoder1RTT (isClient conn) ver cipher secN1 coder- else- genNiteCoder1RTT (isClient conn) ver cipher secN1 coder+ coderN1 <-+ if useFusion && avail+ then genFusionCoder1RTT (isClient conn) ver cipher secN1 coder+ else genNiteCoder1RTT (isClient conn) ver cipher secN1 coder let nextCoder = Coder1RTT coderN1 secN1 writeArray (coders1RTT conn) nextPhase nextCoder -updateSecret :: Version -> Cipher -> TrafficSecrets ApplicationSecret -> TrafficSecrets ApplicationSecret+updateSecret+ :: Version+ -> Cipher+ -> TrafficSecrets ApplicationSecret+ -> TrafficSecrets ApplicationSecret updateSecret ver cipher (ClientTrafficSecret cN, ServerTrafficSecret sN) = secN1 where Secret cN1 = nextSecret ver cipher $ Secret cN Secret sN1 = nextSecret ver cipher $ Secret sN secN1 = (ClientTrafficSecret cN1, ServerTrafficSecret sN1) -genFusionCoder :: Bool -> Version -> Cipher -> TrafficSecrets a -> IO (Coder, Protector)+genFusionCoder+ :: Bool -> Version -> Cipher -> TrafficSecrets a -> IO (Coder, Protector) genFusionCoder cli ver cipher (ClientTrafficSecret c, ServerTrafficSecret s) = do fctxt <- fusionNewContext fctxr <- fusionNewContext fusionSetup cipher fctxt txPayloadKey txPayloadIV fusionSetup cipher fctxr rxPayloadKey rxPayloadIV supp <- fusionSetupSupplement cipher txHeaderKey- let coder = Coder {- encrypt = fusionEncrypt fctxt supp- , decrypt = fusionDecrypt fctxr- , supplement = Just supp- }- let protector = Protector {- setSample = fusionSetSample supp- , getMask = fusionGetMask supp- , unprotect = unp- }+ let coder =+ Coder+ { encrypt = fusionEncrypt fctxt supp+ , decrypt = fusionDecrypt fctxr+ , supplement = Just supp+ }+ let protector =+ Protector+ { setSample = fusionSetSample supp+ , getMask = fusionGetMask supp+ , unprotect = unp+ } return (coder, protector) where- txSecret | cli = Secret c- | otherwise = Secret s- rxSecret | cli = Secret s- | otherwise = Secret c+ txSecret+ | cli = Secret c+ | otherwise = Secret s+ rxSecret+ | cli = Secret s+ | otherwise = Secret c txPayloadKey = aeadKey ver cipher txSecret- txPayloadIV = initialVector ver cipher txSecret- txHeaderKey = headerProtectionKey ver cipher txSecret+ txPayloadIV = initialVector ver cipher txSecret+ txHeaderKey = headerProtectionKey ver cipher txSecret rxPayloadKey = aeadKey ver cipher rxSecret- rxPayloadIV = initialVector ver cipher rxSecret- rxHeaderKey = headerProtectionKey ver cipher rxSecret+ rxPayloadIV = initialVector ver cipher rxSecret+ rxHeaderKey = headerProtectionKey ver cipher rxSecret unp = protectionMask cipher rxHeaderKey -genNiteCoder :: Bool -> Version -> Cipher -> TrafficSecrets a -> IO (Coder, Protector)+genNiteCoder+ :: Bool -> Version -> Cipher -> TrafficSecrets a -> IO (Coder, Protector) genNiteCoder cli ver cipher (ClientTrafficSecret c, ServerTrafficSecret s) = do let enc = makeNiteEncrypt cipher txPayloadKey txPayloadIV dec = makeNiteDecrypt cipher rxPayloadKey rxPayloadIV- (set,get) <- makeNiteProtector cipher txHeaderKey- let coder = Coder {- encrypt = enc- , decrypt = dec- , supplement = Nothing- }- let protector = Protector {- setSample = set- , getMask = get- , unprotect = unp- }+ (set, get) <- makeNiteProtector cipher txHeaderKey+ let coder =+ Coder+ { encrypt = enc+ , decrypt = dec+ , supplement = Nothing+ }+ let protector =+ Protector+ { setSample = set+ , getMask = get+ , unprotect = unp+ } return (coder, protector) where- txSecret | cli = Secret c- | otherwise = Secret s- rxSecret | cli = Secret s- | otherwise = Secret c+ txSecret+ | cli = Secret c+ | otherwise = Secret s+ rxSecret+ | cli = Secret s+ | otherwise = Secret c txPayloadKey = aeadKey ver cipher txSecret- txPayloadIV = initialVector ver cipher txSecret- txHeaderKey = headerProtectionKey ver cipher txSecret+ txPayloadIV = initialVector ver cipher txSecret+ txHeaderKey = headerProtectionKey ver cipher txSecret rxPayloadKey = aeadKey ver cipher rxSecret- rxPayloadIV = initialVector ver cipher rxSecret- rxHeaderKey = headerProtectionKey ver cipher rxSecret+ rxPayloadIV = initialVector ver cipher rxSecret+ rxHeaderKey = headerProtectionKey ver cipher rxSecret unp = protectionMask cipher rxHeaderKey -genFusionCoder1RTT :: Bool -> Version -> Cipher -> TrafficSecrets a -> Coder -> IO Coder+genFusionCoder1RTT+ :: Bool -> Version -> Cipher -> TrafficSecrets a -> Coder -> IO Coder genFusionCoder1RTT cli ver cipher (ClientTrafficSecret c, ServerTrafficSecret s) oldcoder = do fctxt <- fusionNewContext fctxr <- fusionNewContext fusionSetup cipher fctxt txPayloadKey txPayloadIV fusionSetup cipher fctxr rxPayloadKey rxPayloadIV let supp = fromJust $ supplement oldcoder- let coder = Coder {- encrypt = fusionEncrypt fctxt supp- , decrypt = fusionDecrypt fctxr- , supplement = Just supp- }+ let coder =+ Coder+ { encrypt = fusionEncrypt fctxt supp+ , decrypt = fusionDecrypt fctxr+ , supplement = Just supp+ } return coder where- txSecret | cli = Secret c- | otherwise = Secret s- rxSecret | cli = Secret s- | otherwise = Secret c+ txSecret+ | cli = Secret c+ | otherwise = Secret s+ rxSecret+ | cli = Secret s+ | otherwise = Secret c txPayloadKey = aeadKey ver cipher txSecret- txPayloadIV = initialVector ver cipher txSecret+ txPayloadIV = initialVector ver cipher txSecret rxPayloadKey = aeadKey ver cipher rxSecret- rxPayloadIV = initialVector ver cipher rxSecret+ rxPayloadIV = initialVector ver cipher rxSecret -genNiteCoder1RTT :: Bool -> Version -> Cipher -> TrafficSecrets a -> Coder -> IO Coder+genNiteCoder1RTT+ :: Bool -> Version -> Cipher -> TrafficSecrets a -> Coder -> IO Coder genNiteCoder1RTT cli ver cipher (ClientTrafficSecret c, ServerTrafficSecret s) _oldcoder = do let enc = makeNiteEncrypt cipher txPayloadKey txPayloadIV dec = makeNiteDecrypt cipher rxPayloadKey rxPayloadIV- let coder = Coder {- encrypt = enc- , decrypt = dec- , supplement = Nothing- }+ let coder =+ Coder+ { encrypt = enc+ , decrypt = dec+ , supplement = Nothing+ } return coder where- txSecret | cli = Secret c- | otherwise = Secret s- rxSecret | cli = Secret s- | otherwise = Secret c+ txSecret+ | cli = Secret c+ | otherwise = Secret s+ rxSecret+ | cli = Secret s+ | otherwise = Secret c txPayloadKey = aeadKey ver cipher txSecret- txPayloadIV = initialVector ver cipher txSecret+ txPayloadIV = initialVector ver cipher txSecret rxPayloadKey = aeadKey ver cipher rxSecret- rxPayloadIV = initialVector ver cipher rxSecret+ rxPayloadIV = initialVector ver cipher rxSecret getCoder :: Connection -> EncryptionLevel -> Bool -> IO Coder getCoder conn RTT1Level k = coder1RTT <$> readArray (coders1RTT conn) k-getCoder conn lvl _ = readArray (coders conn) lvl+getCoder conn lvl _ = readArray (coders conn) lvl getProtector :: Connection -> EncryptionLevel -> IO Protector getProtector conn lvl = readArray (protectors conn) lvl
Network/QUIC/Connection/Migration.hs view
@@ -1,30 +1,30 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Connection.Migration (- getMyCID- , getMyCIDs- , getPeerCID- , isMyCID- , myCIDsInclude- , shouldUpdateMyCID- , shouldUpdatePeerCID- , resetPeerCID- , getNewMyCID- , getMyCIDSeqNum- , setMyCID- , setPeerCIDAndRetireCIDs- , retirePeerCID- , retireMyCID- , addPeerCID- , waitPeerCID- , choosePeerCIDForPrivacy- , setPeerStatelessResetToken- , isStatelessRestTokenValid- , setMigrationStarted- , isPathValidating- , checkResponse- , validatePath- ) where+ getMyCID,+ getMyCIDs,+ getPeerCID,+ isMyCID,+ myCIDsInclude,+ shouldUpdateMyCID,+ shouldUpdatePeerCID,+ resetPeerCID,+ getNewMyCID,+ getMyCIDSeqNum,+ setMyCID,+ setPeerCIDAndRetireCIDs,+ retirePeerCID,+ retireMyCID,+ addPeerCID,+ waitPeerCID,+ choosePeerCIDForPrivacy,+ setPeerStatelessResetToken,+ isStatelessRestTokenValid,+ setMigrationStarted,+ isPathValidating,+ checkResponse,+ validatePath,+) where import qualified Data.IntMap.Strict as IntMap import qualified Data.Map.Strict as Map@@ -85,8 +85,8 @@ addPeerCID Connection{..} cidInfo = atomically $ do db <- readTVar peerCIDDB case Map.lookup (cidInfoCID cidInfo) (revInfos db) of- Nothing -> modifyTVar' peerCIDDB $ add cidInfo- Just _ -> return ()+ Nothing -> modifyTVar' peerCIDDB $ add cidInfo+ Just _ -> return () shouldUpdatePeerCID :: Connection -> IO Bool shouldUpdatePeerCID Connection{..} =@@ -98,14 +98,14 @@ mr <- atomically $ do mncid <- pickPeerCID conn case mncid of- Nothing -> return ()- Just ncid -> do- setPeerCID conn ncid False- return ()+ Nothing -> return ()+ Just ncid -> do+ setPeerCID conn ncid False+ return () return mncid case mr of- Nothing -> return ()- Just ncid -> qlogCIDUpdate conn $ Remote $ cidInfoCID ncid+ Nothing -> return ()+ Just ncid -> qlogCIDUpdate conn $ Remote $ cidInfoCID ncid -- | Only for the internal "migration" API waitPeerCID :: Connection -> IO CIDInfo@@ -151,18 +151,20 @@ arrange n db@CIDDB{..} = (db', dropSeqnums) where (toDrops, cidInfos') = IntMap.partitionWithKey (\k _ -> k < n) cidInfos- dropSeqnums = IntMap.foldrWithKey (\k _ ks -> k:ks) [] toDrops+ dropSeqnums = IntMap.foldrWithKey (\k _ ks -> k : ks) [] toDrops dropCIDs = IntMap.foldr (\c r -> cidInfoCID c : r) [] toDrops -- IntMap.findMin is a partial function. -- But receiver guarantees that there is at least one cidinfo.- usedCIDInfo' | cidInfoSeq usedCIDInfo >= n = usedCIDInfo- | otherwise = snd $ IntMap.findMin cidInfos'+ usedCIDInfo'+ | cidInfoSeq usedCIDInfo >= n = usedCIDInfo+ | otherwise = snd $ IntMap.findMin cidInfos' revInfos' = foldr Map.delete revInfos dropCIDs- db' = db {- usedCIDInfo = usedCIDInfo'- , cidInfos = cidInfos'- , revInfos = revInfos'- }+ db' =+ db+ { usedCIDInfo = usedCIDInfo'+ , cidInfos = cidInfos'+ , revInfos = revInfos'+ } ---------------------------------------------------------------- @@ -173,12 +175,12 @@ when r $ qlogCIDUpdate conn $ Local ncid where findSet db@CIDDB{..}- | cidInfoCID usedCIDInfo == ncid = (db, False)- | otherwise = case Map.lookup ncid revInfos of- Nothing -> (db, False)- Just n -> case IntMap.lookup n cidInfos of+ | cidInfoCID usedCIDInfo == ncid = (db, False)+ | otherwise = case Map.lookup ncid revInfos of Nothing -> (db, False)- Just ncidinfo -> (set ncidinfo False db, True)+ Just n -> case IntMap.lookup n cidInfos of+ Nothing -> (db, False)+ Just ncidinfo -> (set ncidinfo False db, True) -- | Receiving RetireConnectionID retireMyCID :: Connection -> Int -> IO (Maybe CIDInfo)@@ -189,49 +191,54 @@ set :: CIDInfo -> Bool -> CIDDB -> CIDDB set cidInfo pri db = db' where- db' = db {- usedCIDInfo = cidInfo- , triggeredByMe = pri- }+ db' =+ db+ { usedCIDInfo = cidInfo+ , triggeredByMe = pri+ } add :: CIDInfo -> CIDDB -> CIDDB add cidInfo@CIDInfo{..} db@CIDDB{..} = db' where- db' = db {- cidInfos = IntMap.insert cidInfoSeq cidInfo cidInfos- , revInfos = Map.insert cidInfoCID cidInfoSeq revInfos- }+ db' =+ db+ { cidInfos = IntMap.insert cidInfoSeq cidInfo cidInfos+ , revInfos = Map.insert cidInfoCID cidInfoSeq revInfos+ } new :: CID -> StatelessResetToken -> CIDDB -> (CIDDB, CIDInfo) new cid srt db@CIDDB{..} = (db', cidInfo) where- cidInfo = CIDInfo nextSeqNum cid srt- db' = db {- nextSeqNum = nextSeqNum + 1- , cidInfos = IntMap.insert nextSeqNum cidInfo cidInfos- , revInfos = Map.insert cid nextSeqNum revInfos- }+ cidInfo = CIDInfo nextSeqNum cid srt+ db' =+ db+ { nextSeqNum = nextSeqNum + 1+ , cidInfos = IntMap.insert nextSeqNum cidInfo cidInfos+ , revInfos = Map.insert cid nextSeqNum revInfos+ } del :: Int -> CIDDB -> CIDDB del n db@CIDDB{..} = db' where db' = case IntMap.lookup n cidInfos of- Nothing -> db- Just cidInfo -> db {- cidInfos = IntMap.delete n cidInfos- , revInfos = Map.delete (cidInfoCID cidInfo) revInfos- }+ Nothing -> db+ Just cidInfo ->+ db+ { cidInfos = IntMap.delete n cidInfos+ , revInfos = Map.delete (cidInfoCID cidInfo) revInfos+ } del' :: Int -> CIDDB -> (CIDDB, Maybe CIDInfo) del' n db@CIDDB{..} = (db', mcidInfo) where mcidInfo = IntMap.lookup n cidInfos db' = case mcidInfo of- Nothing -> db- Just cidInfo -> db {- cidInfos = IntMap.delete n cidInfos- , revInfos = Map.delete (cidInfoCID cidInfo) revInfos- }+ Nothing -> db+ Just cidInfo ->+ db+ { cidInfos = IntMap.delete n cidInfos+ , revInfos = Map.delete (cidInfoCID cidInfo) revInfos+ } ---------------------------------------------------------------- @@ -242,22 +249,24 @@ adjust db@CIDDB{..} = db' where db' = case IntMap.lookup 0 cidInfos of- Nothing -> db- Just cidinfo -> let cidinfo' = cidinfo { cidInfoSRT = srt }- in db {- cidInfos = IntMap.insert 0 cidinfo'- $ IntMap.delete 0 cidInfos- , usedCIDInfo = cidinfo'- }+ Nothing -> db+ Just cidinfo ->+ let cidinfo' = cidinfo{cidInfoSRT = srt}+ in db+ { cidInfos =+ IntMap.insert 0 cidinfo' $+ IntMap.delete 0 cidInfos+ , usedCIDInfo = cidinfo'+ } isStatelessRestTokenValid :: Connection -> CID -> StatelessResetToken -> IO Bool isStatelessRestTokenValid Connection{..} cid srt = srtCheck <$> readTVarIO peerCIDDB where srtCheck CIDDB{..} = case Map.lookup cid revInfos of- Nothing -> False- Just n -> case IntMap.lookup n cidInfos of Nothing -> False- Just (CIDInfo _ _ srt0) -> srt == srt0+ Just n -> case IntMap.lookup n cidInfos of+ Nothing -> False+ Just (CIDInfo _ _ srt0) -> srt == srt0 ---------------------------------------------------------------- @@ -270,7 +279,9 @@ validatePath conn (Just (CIDInfo retiredSeqNum _ _)) = do pdat <- newPathData setChallenges conn [pdat]- putOutput conn $ OutControl RTT1Level [PathChallenge pdat, RetireConnectionID retiredSeqNum] $ return ()+ putOutput conn $+ OutControl RTT1Level [PathChallenge pdat, RetireConnectionID retiredSeqNum] $+ return () waitResponse conn retirePeerCID conn retiredSeqNum @@ -286,9 +297,9 @@ isPathValidating Connection{..} = do s <- readTVarIO migrationState case s of- SendChallenge _ -> return True- MigrationStarted -> return True- _ -> return False+ SendChallenge _ -> return True+ MigrationStarted -> return True+ _ -> return False waitResponse :: Connection -> IO () waitResponse Connection{..} = atomically $ do@@ -300,6 +311,6 @@ checkResponse Connection{..} pdat = do state <- readTVarIO migrationState case state of- SendChallenge pdats- | pdat `elem` pdats -> atomically $ writeTVar migrationState RecvResponse- _ -> return ()+ SendChallenge pdats+ | pdat `elem` pdats -> atomically $ writeTVar migrationState RecvResponse+ _ -> return ()
Network/QUIC/Connection/Misc.hs view
@@ -2,33 +2,33 @@ {-# LANGUAGE TupleSections #-} module Network.QUIC.Connection.Misc (- setVersionInfo- , getVersionInfo- , setVersion- , getVersion- , getOriginalVersion- , getSocket- , setSocket- , clearSocket- , getPeerAuthCIDs- , setPeerAuthCIDs- , getClientDstCID- , getMyParameters- , getPeerParameters- , setPeerParameters- , delayedAck- , resetDealyedAck- , setMaxPacketSize- , addReader- , killReaders- , addResource- , freeResources- , readMinIdleTimeout- , setMinIdleTimeout- , sendFrames- , closeConnection- , abortConnection- ) where+ setVersionInfo,+ getVersionInfo,+ setVersion,+ getVersion,+ getOriginalVersion,+ getSocket,+ setSocket,+ clearSocket,+ getPeerAuthCIDs,+ setPeerAuthCIDs,+ getClientDstCID,+ getMyParameters,+ getPeerParameters,+ setPeerParameters,+ delayedAck,+ resetDealyedAck,+ setMaxPacketSize,+ addReader,+ killReaders,+ addResource,+ freeResources,+ readMinIdleTimeout,+ setMinIdleTimeout,+ sendFrames,+ closeConnection,+ abortConnection,+) where import Network.UDP import System.Mem.Weak@@ -53,7 +53,7 @@ setVersion :: Connection -> Version -> IO () setVersion Connection{..} ver = atomicModifyIORef'' quicVersionInfo $ \vi ->- vi { chosenVersion = ver }+ vi{chosenVersion = ver} getVersion :: Connection -> IO Version getVersion conn = chosenVersion <$> getVersionInfo conn@@ -86,13 +86,13 @@ getClientDstCID :: Connection -> IO CID getClientDstCID conn = do- cids <- if isClient conn then- getPeerAuthCIDs conn- else- getMyAuthCIDs conn+ cids <-+ if isClient conn+ then getPeerAuthCIDs conn+ else getMyAuthCIDs conn return $ case retrySrcCID cids of- Nothing -> fromJust $ origDstCID cids- Just dcid -> dcid+ Nothing -> fromJust $ origDstCID cids+ Just dcid -> dcid ---------------------------------------------------------------- @@ -111,7 +111,7 @@ delayedAck :: Connection -> IO () delayedAck conn@Connection{..} = do- (oldcnt,send_) <- atomicModifyIORef' delayedAckCount check+ (oldcnt, send_) <- atomicModifyIORef' delayedAckCount check when (oldcnt == 0) $ do new <- cfire conn (Microseconds 20000) sendAck join $ atomicModifyIORef' delayedAckCancel (new,)@@ -121,8 +121,8 @@ sendAck where sendAck = putOutput conn $ OutControl RTT1Level [] $ return ()- check 1 = (0, (1, True))- check n = (n+1, (n, False))+ check 1 = (0, (1, True))+ check n = (n + 1, (n, False)) resetDealyedAck :: Connection -> IO () resetDealyedAck Connection{..} = do@@ -165,8 +165,8 @@ setMinIdleTimeout :: Connection -> Microseconds -> IO () setMinIdleTimeout Connection{..} us- | us == Microseconds 0 = return ()- | otherwise = atomicModifyIORef'' minIdleTimeout modify+ | us == Microseconds 0 = return ()+ | otherwise = atomicModifyIORef'' minIdleTimeout modify where modify us0 = min us us0 @@ -183,5 +183,6 @@ quicexc = TransportErrorIsSent err desc -- | Closing a connection with an application protocol error.-abortConnection :: Connection -> ApplicationProtocolError -> ReasonPhrase -> IO ()+abortConnection+ :: Connection -> ApplicationProtocolError -> ReasonPhrase -> IO () abortConnection conn err desc = E.throwTo (mainThreadId conn) $ Abort err desc
Network/QUIC/Connection/PacketNumber.hs view
@@ -1,10 +1,10 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Connection.PacketNumber (- nextPacketNumber- , setPeerPacketNumber- , getPeerPacketNumber- ) where+ nextPacketNumber,+ setPeerPacketNumber,+ getPeerPacketNumber,+) where import Network.QUIC.Connection.Types import Network.QUIC.Connector
Network/QUIC/Connection/Role.hs view
@@ -2,25 +2,25 @@ {-# OPTIONS_GHC -Wno-incomplete-record-updates #-} module Network.QUIC.Connection.Role (- setToken- , getToken- , getResumptionInfo- , setRetried- , getRetried- , setIncompatibleVN- , getIncompatibleVN- , setResumptionSession- , setNewToken- , setRegister- , getRegister- , getUnregister- , setTokenManager- , getTokenManager- , setBaseThreadId- , getBaseThreadId- , setCertificateChain- , getCertificateChain- ) where+ setToken,+ getToken,+ getResumptionInfo,+ setRetried,+ getRetried,+ setIncompatibleVN,+ getIncompatibleVN,+ setResumptionSession,+ setNewToken,+ setRegister,+ getRegister,+ getUnregister,+ setTokenManager,+ getTokenManager,+ setBaseThreadId,+ getBaseThreadId,+ setCertificateChain,+ getCertificateChain,+) where import qualified Crypto.Token as CT import Data.X509 (CertificateChain)@@ -36,12 +36,12 @@ setToken :: Connection -> Token -> IO () setToken Connection{..} token = atomicModifyIORef'' roleInfo $- \ci -> ci { clientInitialToken = token }+ \ci -> ci{clientInitialToken = token} getToken :: Connection -> IO Token getToken conn@Connection{..}- | isClient conn = clientInitialToken <$> readIORef roleInfo- | otherwise = return emptyToken+ | isClient conn = clientInitialToken <$> readIORef roleInfo+ | otherwise = return emptyToken ---------------------------------------------------------------- @@ -53,59 +53,67 @@ setRetried :: Connection -> Bool -> IO () setRetried conn@Connection{..} r- | isClient conn = atomicModifyIORef'' roleInfo $ \ci -> ci {- resumptionInfo = (resumptionInfo ci) { resumptionRetry = r}- }- | otherwise = atomicModifyIORef'' roleInfo $ \si -> si { askRetry = r }+ | isClient conn = atomicModifyIORef'' roleInfo $ \ci ->+ ci+ { resumptionInfo = (resumptionInfo ci){resumptionRetry = r}+ }+ | otherwise = atomicModifyIORef'' roleInfo $ \si -> si{askRetry = r} getRetried :: Connection -> IO Bool getRetried conn@Connection{..}- | isClient conn = resumptionRetry . resumptionInfo <$> readIORef roleInfo- | otherwise = askRetry <$> readIORef roleInfo+ | isClient conn = resumptionRetry . resumptionInfo <$> readIORef roleInfo+ | otherwise = askRetry <$> readIORef roleInfo ---------------------------------------------------------------- setIncompatibleVN :: Connection -> Bool -> IO () setIncompatibleVN conn@Connection{..} icvn- | isClient conn = atomicModifyIORef'' roleInfo $ \ci -> ci {- incompatibleVN = icvn- }- | otherwise = return ()+ | isClient conn = atomicModifyIORef'' roleInfo $ \ci ->+ ci+ { incompatibleVN = icvn+ }+ | otherwise = return () getIncompatibleVN :: Connection -> IO Bool getIncompatibleVN conn@Connection{..}- | isClient conn = incompatibleVN <$> readIORef roleInfo- | otherwise = return False+ | isClient conn = incompatibleVN <$> readIORef roleInfo+ | otherwise = return False ---------------------------------------------------------------- setResumptionSession :: Connection -> SessionEstablish setResumptionSession conn@Connection{..} si sd = do ver <- getVersion conn- atomicModifyIORef'' roleInfo $ \ci -> ci {- resumptionInfo = (resumptionInfo ci) {- resumptionVersion = ver- , resumptionSession = Just (si,sd)+ atomicModifyIORef'' roleInfo $ \ci ->+ ci+ { resumptionInfo =+ (resumptionInfo ci)+ { resumptionVersion = ver+ , resumptionSession = Just (si, sd)+ } }- } setNewToken :: Connection -> Token -> IO () setNewToken conn@Connection{..} token = do ver <- getVersion conn- atomicModifyIORef'' roleInfo $ \ci -> ci {- resumptionInfo = (resumptionInfo ci) {- resumptionVersion = ver- , resumptionToken = token+ atomicModifyIORef'' roleInfo $ \ci ->+ ci+ { resumptionInfo =+ (resumptionInfo ci)+ { resumptionVersion = ver+ , resumptionToken = token+ } }- } ---------------------------------------------------------------- -setRegister :: Connection -> (CID -> Connection -> IO ()) -> (CID -> IO ()) -> IO ()-setRegister Connection{..} regisrer unregister = atomicModifyIORef'' roleInfo $ \si -> si {- registerCID = regisrer- , unregisterCID = unregister- }+setRegister+ :: Connection -> (CID -> Connection -> IO ()) -> (CID -> IO ()) -> IO ()+setRegister Connection{..} regisrer unregister = atomicModifyIORef'' roleInfo $ \si ->+ si+ { registerCID = regisrer+ , unregisterCID = unregister+ } getRegister :: Connection -> IO (CID -> Connection -> IO ()) getRegister Connection{..} = registerCID <$> readIORef roleInfo@@ -117,7 +125,7 @@ setTokenManager :: Connection -> CT.TokenManager -> IO () setTokenManager Connection{..} mgr = atomicModifyIORef'' roleInfo $- \si -> si { tokenManager = mgr }+ \si -> si{tokenManager = mgr} getTokenManager :: Connection -> IO CT.TokenManager getTokenManager Connection{..} = tokenManager <$> readIORef roleInfo@@ -126,7 +134,7 @@ setBaseThreadId :: Connection -> ThreadId -> IO () setBaseThreadId Connection{..} tid = atomicModifyIORef'' roleInfo $- \si -> si { baseThreadId = tid }+ \si -> si{baseThreadId = tid} getBaseThreadId :: Connection -> IO ThreadId getBaseThreadId Connection{..} = baseThreadId <$> readIORef roleInfo@@ -135,7 +143,7 @@ setCertificateChain :: Connection -> Maybe CertificateChain -> IO () setCertificateChain Connection{..} mcc = atomicModifyIORef'' roleInfo $- \si -> si { certChain = mcc }+ \si -> si{certChain = mcc} getCertificateChain :: Connection -> IO (Maybe CertificateChain) getCertificateChain Connection{..} = certChain <$> readIORef roleInfo
Network/QUIC/Connection/State.hs view
@@ -1,41 +1,36 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Connection.State (- setConnection0RTTReady- , isConnection1RTTReady- , setConnection1RTTReady- , isConnectionEstablished- , setConnectionEstablished- , wait0RTTReady- , wait1RTTReady- , waitEstablished- , readConnectionFlowTx- , addTxData- , getTxData- , setTxMaxData- , getTxMaxData- , addRxData- , getRxData- , addRxMaxData- , getRxMaxData- , getRxDataWindow- , checkRxMaxData- , addTxBytes- , getTxBytes- , addRxBytes- , getRxBytes- , setAddressValidated- , waitAntiAmplificationFree- , checkAntiAmplificationFree- ) where+ setConnection0RTTReady,+ isConnection1RTTReady,+ setConnection1RTTReady,+ isConnectionEstablished,+ setConnectionEstablished,+ wait0RTTReady,+ wait1RTTReady,+ waitEstablished,+ readConnectionFlowTx,+ addTxData,+ setTxMaxData,+ getRxMaxData,+ updateFlowRx,+ checkRxMaxData,+ addTxBytes,+ getTxBytes,+ addRxBytes,+ getRxBytes,+ setAddressValidated,+ waitAntiAmplificationFree,+ checkAntiAmplificationFree,+) where +import Network.Control import UnliftIO.STM import Network.QUIC.Connection.Types import Network.QUIC.Connector import Network.QUIC.Imports import Network.QUIC.Recovery-import Network.QUIC.Stream ---------------------------------------------------------------- @@ -84,7 +79,7 @@ ---------------------------------------------------------------- -readConnectionFlowTx :: Connection -> STM Flow+readConnectionFlowTx :: Connection -> STM TxFlow readConnectionFlowTx Connection{..} = readTVar flowTx ----------------------------------------------------------------@@ -92,55 +87,27 @@ addTxData :: Connection -> Int -> STM () addTxData Connection{..} n = modifyTVar' flowTx add where- add flow = flow { flowData = flowData flow + n }--getTxData :: Connection -> IO Int-getTxData Connection{..} = atomically $ flowData <$> readTVar flowTx+ add flow = flow{txfSent = txfSent flow + n} setTxMaxData :: Connection -> Int -> IO () setTxMaxData Connection{..} n = atomically $ modifyTVar' flowTx set where set flow- | flowMaxData flow < n = flow { flowMaxData = n }- | otherwise = flow--getTxMaxData :: Connection -> STM Int-getTxMaxData Connection{..} = flowMaxData <$> readTVar flowTx+ | txfLimit flow < n = flow{txfLimit = n}+ | otherwise = flow ---------------------------------------------------------------- -addRxData :: Connection -> Int -> IO ()-addRxData Connection{..} n = atomicModifyIORef'' flowRx add- where- add flow = flow { flowData = flowData flow + n }--getRxData :: Connection -> IO Int-getRxData Connection{..} = flowData <$> readIORef flowRx--addRxMaxData :: Connection -> Int -> IO Int-addRxMaxData Connection{..} n = atomicModifyIORef' flowRx add- where- add flow = (flow { flowMaxData = m }, m)- where- m = flowMaxData flow + n- getRxMaxData :: Connection -> IO Int-getRxMaxData Connection{..} = flowMaxData <$> readIORef flowRx--getRxDataWindow :: Connection -> IO Int-getRxDataWindow Connection{..} = flowWindow <$> readIORef flowRx+getRxMaxData Connection{..} = rxfLimit <$> readIORef flowRx -----------------------------------------------------------------+updateFlowRx :: Connection -> Int -> IO (Maybe Int)+updateFlowRx Connection{..} consumed =+ atomicModifyIORef' flowRx $ maybeOpenRxWindow consumed FCTMaxData checkRxMaxData :: Connection -> Int -> IO Bool-checkRxMaxData Connection{..} len = do- received <- readIORef flowBytesRx- maxData <- flowMaxData <$> readIORef flowRx- if received + len < maxData then do- modifyIORef' flowBytesRx (+ len)- return True- else- return False+checkRxMaxData Connection{..} len =+ atomicModifyIORef' flowRx $ checkRxLimit len ---------------------------------------------------------------- @@ -168,17 +135,18 @@ unless ok $ do beforeAntiAmp connLDCC atomically (checkAntiAmplificationFreeSTM conn siz >>= checkSTM)- -- setLossDetectionTimer is called eventually. +-- setLossDetectionTimer is called eventually.+ checkAntiAmplificationFreeSTM :: Connection -> Int -> STM Bool checkAntiAmplificationFreeSTM Connection{..} siz = do validated <- readTVar addressValidated- if validated then- return True- else do- tx <- readTVar bytesTx- rx <- readTVar bytesRx- return (tx + siz <= 3 * rx)+ if validated+ then return True+ else do+ tx <- readTVar bytesTx+ rx <- readTVar bytesRx+ return (tx + siz <= 3 * rx) checkAntiAmplificationFree :: Connection -> Int -> IO Bool checkAntiAmplificationFree conn siz =
Network/QUIC/Connection/Stream.hs view
@@ -3,15 +3,15 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Connection.Stream (- getMyStreamId- , waitMyNewStreamId- , waitMyNewUniStreamId- , setTxMaxStreams- , setTxUniMaxStreams- , checkRxMaxStreams- , updatePeerStreamId- , checkStreamIdRoom- ) where+ getMyStreamId,+ waitMyNewStreamId,+ waitMyNewUniStreamId,+ setTxMaxStreams,+ setTxUniMaxStreams,+ checkRxMaxStreams,+ updatePeerStreamId,+ checkStreamIdRoom,+) where import UnliftIO.STM @@ -40,7 +40,7 @@ StreamIdBase base = maxStreams checkSTM (currentStream < base * 4 + streamType) let currentStream' = currentStream + 4- writeTVar tvar conc { currentStream = currentStream' }+ writeTVar tvar conc{currentStream = currentStream'} return currentStream -- From "Peer", but set it to "My".@@ -52,20 +52,26 @@ setTxUniMaxStreams Connection{..} = set myUniStreamId set :: TVar Concurrency -> Int -> IO ()-set tvar mx = atomically $ modifyTVar tvar $ \c -> c { maxStreams = StreamIdBase mx }+set tvar mx = atomically $ modifyTVar tvar $ \c -> c{maxStreams = StreamIdBase mx} updatePeerStreamId :: Connection -> StreamId -> IO () updatePeerStreamId conn sid = do- when ((isClient conn && isServerInitiatedBidirectional sid)- || (isServer conn && isClientInitiatedBidirectional sid)) $ do- atomicModifyIORef'' (peerStreamId conn) check- when ((isClient conn && isServerInitiatedUnidirectional sid)- || (isServer conn && isClientInitiatedUnidirectional sid)) $ do- atomicModifyIORef'' (peerUniStreamId conn) check+ when+ ( (isClient conn && isServerInitiatedBidirectional sid)+ || (isServer conn && isClientInitiatedBidirectional sid)+ )+ $ do+ atomicModifyIORef'' (peerStreamId conn) check+ when+ ( (isClient conn && isServerInitiatedUnidirectional sid)+ || (isServer conn && isClientInitiatedUnidirectional sid)+ )+ $ do+ atomicModifyIORef'' (peerUniStreamId conn) check where check conc@Concurrency{..}- | currentStream < sid = conc { currentStream = sid }- | otherwise = conc+ | currentStream < sid = conc{currentStream = sid}+ | otherwise = conc checkRxMaxStreams :: Connection -> StreamId -> IO Bool checkRxMaxStreams conn@Connection{..} sid = do@@ -76,30 +82,31 @@ where streamType = sid .&. 0b11 readForClient = case streamType of- 0 -> readTVarIO myStreamId- 1 -> readIORef peerStreamId- 2 -> readTVarIO myUniStreamId- 3 -> readIORef peerUniStreamId- _ -> error "never reach"+ 0 -> readTVarIO myStreamId+ 1 -> readIORef peerStreamId+ 2 -> readTVarIO myUniStreamId+ 3 -> readIORef peerUniStreamId+ _ -> error "never reach" readForServer = case streamType of- 0 -> readIORef peerStreamId- 1 -> readTVarIO myStreamId- 2 -> readIORef peerUniStreamId- 3 -> readTVarIO myUniStreamId- _ -> error "never reach"+ 0 -> readIORef peerStreamId+ 1 -> readTVarIO myStreamId+ 2 -> readIORef peerUniStreamId+ 3 -> readTVarIO myUniStreamId+ _ -> error "never reach" checkStreamIdRoom :: Connection -> Direction -> IO (Maybe Int) checkStreamIdRoom conn dir = do- let ref | dir == Bidirectional = peerStreamId conn- | otherwise = peerUniStreamId conn+ let ref+ | dir == Bidirectional = peerStreamId conn+ | otherwise = peerUniStreamId conn atomicModifyIORef' ref check where check conc@Concurrency{..} = let StreamIdBase base = maxStreams initialStreams = initialMaxStreamsBidi $ getMyParameters conn cbase = currentStream !>>. 2- in if (base - cbase < (initialStreams !>>. 3)) then- let base' = base + initialStreams- in (conc { maxStreams = StreamIdBase base' }, Just base')- else- (conc, Nothing)+ in if (base - cbase < (initialStreams !>>. 3))+ then+ let base' = cbase + initialStreams+ in (conc{maxStreams = StreamIdBase base'}, Just base')+ else (conc, Nothing)
Network/QUIC/Connection/StreamTable.hs view
@@ -1,15 +1,15 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Connection.StreamTable (- createStream- , findStream- , addStream- , delStream- , initialRxMaxStreamData- , setupCryptoStreams- , clearCryptoStream- , getCryptoStream- ) where+ createStream,+ findStream,+ addStream,+ delStream,+ initialRxMaxStreamData,+ setupCryptoStreams,+ clearCryptoStream,+ getCryptoStream,+) where import Network.QUIC.Connection.Misc import Network.QUIC.Connection.Queue@@ -22,26 +22,29 @@ createStream :: Connection -> StreamId -> IO Stream createStream conn sid = do- strm <- addStream conn sid- putInput conn $ InpStream strm- return strm+ strm <- addStream conn sid+ putInput conn $ InpStream strm+ return strm findStream :: Connection -> StreamId -> IO (Maybe Stream) findStream Connection{..} sid = lookupStream sid <$> readIORef streamTable addStream :: Connection -> StreamId -> IO Stream addStream conn@Connection{..} sid = do- strm <- newStream conn sid- if isClient conn then do- let clientParams = getMyParameters conn- setRxMaxStreamData strm $ clientInitial sid clientParams- serverParams <- getPeerParameters conn- setTxMaxStreamData strm $ serverInitial sid serverParams- else do- let serverParams = getMyParameters conn- setRxMaxStreamData strm $ serverInitial sid serverParams- clientParams <- getPeerParameters conn- setTxMaxStreamData strm $ clientInitial sid clientParams+ strm <-+ if isClient conn+ then do+ serverParams <- getPeerParameters conn+ let txLim = serverInitial sid serverParams+ let clientParams = getMyParameters conn+ rxLim = clientInitial sid clientParams+ newStream conn sid txLim rxLim+ else do+ clientParams <- getPeerParameters conn+ let txLim = clientInitial sid clientParams+ serverParams = getMyParameters conn+ rxLim = serverInitial sid serverParams+ newStream conn sid txLim rxLim atomicModifyIORef'' streamTable $ insertStream sid strm return strm @@ -52,23 +55,23 @@ initialRxMaxStreamData :: Connection -> StreamId -> Int initialRxMaxStreamData conn sid | isClient conn = clientInitial sid params- | otherwise = serverInitial sid params+ | otherwise = serverInitial sid params where params = getMyParameters conn clientInitial :: StreamId -> Parameters -> Int clientInitial sid params- | isClientInitiatedBidirectional sid = initialMaxStreamDataBidiLocal params- | isServerInitiatedBidirectional sid = initialMaxStreamDataBidiRemote params- -- intentionally not using isServerInitiatedUnidirectional- | otherwise = initialMaxStreamDataUni params+ | isClientInitiatedBidirectional sid = initialMaxStreamDataBidiLocal params+ | isServerInitiatedBidirectional sid = initialMaxStreamDataBidiRemote params+ -- intentionally not using isServerInitiatedUnidirectional+ | otherwise = initialMaxStreamDataUni params serverInitial :: StreamId -> Parameters -> Int serverInitial sid params- | isServerInitiatedBidirectional sid = initialMaxStreamDataBidiLocal params- | isClientInitiatedBidirectional sid = initialMaxStreamDataBidiRemote params- -- intentionally not using isClientInitiatedUnidirectional- | otherwise = initialMaxStreamDataUni params+ | isServerInitiatedBidirectional sid = initialMaxStreamDataBidiLocal params+ | isClientInitiatedBidirectional sid = initialMaxStreamDataBidiRemote params+ -- intentionally not using isClientInitiatedUnidirectional+ | otherwise = initialMaxStreamDataUni params ----------------------------------------------------------------
Network/QUIC/Connection/Timeout.hs view
@@ -1,14 +1,14 @@ module Network.QUIC.Connection.Timeout (- timeout- , fire- , cfire- , delay- ) where+ timeout,+ fire,+ cfire,+ delay,+) where import Network.QUIC.Event+import qualified System.Timeout as ST import UnliftIO.Concurrent import qualified UnliftIO.Exception as E-import qualified System.Timeout as ST import Network.QUIC.Connection.Types import Network.QUIC.Connector
Network/QUIC/Connection/Types.hs view
@@ -15,6 +15,7 @@ import Data.X509 (CertificateChain) import Foreign.Marshal.Alloc import Foreign.Ptr (nullPtr)+import Network.Control (Rate, RxFlow, TxFlow, newRate, newRxFlow, newTxFlow) import Network.TLS.QUIC import Network.UDP (UDPSocket) import UnliftIO.Concurrent@@ -38,133 +39,147 @@ ---------------------------------------------------------------- -data RoleInfo = ClientInfo { clientInitialToken :: Token -- new or retry token- , resumptionInfo :: ResumptionInfo- , incompatibleVN :: Bool- }- | ServerInfo { tokenManager :: ~CT.TokenManager- , registerCID :: CID -> Connection -> IO ()- , unregisterCID :: CID -> IO ()- , askRetry :: Bool- , baseThreadId :: ~ThreadId- , certChain :: Maybe CertificateChain- }+data RoleInfo+ = ClientInfo+ { clientInitialToken :: Token -- new or retry token+ , resumptionInfo :: ResumptionInfo+ , incompatibleVN :: Bool+ }+ | ServerInfo+ { tokenManager :: ~CT.TokenManager+ , registerCID :: CID -> Connection -> IO ()+ , unregisterCID :: CID -> IO ()+ , askRetry :: Bool+ , baseThreadId :: ~ThreadId+ , certChain :: Maybe CertificateChain+ } defaultClientRoleInfo :: RoleInfo-defaultClientRoleInfo = ClientInfo {- clientInitialToken = emptyToken- , resumptionInfo = defaultResumptionInfo- , incompatibleVN = False- }+defaultClientRoleInfo =+ ClientInfo+ { clientInitialToken = emptyToken+ , resumptionInfo = defaultResumptionInfo+ , incompatibleVN = False+ } defaultServerRoleInfo :: RoleInfo-defaultServerRoleInfo = ServerInfo {- tokenManager = undefined- , registerCID = \_ _ -> return ()- , unregisterCID = \_ -> return ()- , askRetry = False- , baseThreadId = undefined- , certChain = Nothing- }+defaultServerRoleInfo =+ ServerInfo+ { tokenManager = undefined+ , registerCID = \_ _ -> return ()+ , unregisterCID = \_ -> return ()+ , askRetry = False+ , baseThreadId = undefined+ , certChain = Nothing+ } -- fixme: limitation-data CIDDB = CIDDB {- usedCIDInfo :: CIDInfo- , cidInfos :: IntMap CIDInfo- , revInfos :: Map CID Int- , nextSeqNum :: Int -- only for mine (new)- , triggeredByMe :: Bool -- only for peer's- } deriving (Show)+data CIDDB = CIDDB+ { usedCIDInfo :: CIDInfo+ , cidInfos :: IntMap CIDInfo+ , revInfos :: Map CID Int+ , nextSeqNum :: Int -- only for mine (new)+ , triggeredByMe :: Bool -- only for peer's+ }+ deriving (Show) newCIDDB :: CID -> CIDDB-newCIDDB cid = CIDDB {- usedCIDInfo = cidInfo- , cidInfos = IntMap.singleton 0 cidInfo- , revInfos = Map.singleton cid 0- , nextSeqNum = 1- , triggeredByMe = False- }+newCIDDB cid =+ CIDDB+ { usedCIDInfo = cidInfo+ , cidInfos = IntMap.singleton 0 cidInfo+ , revInfos = Map.singleton cid 0+ , nextSeqNum = 1+ , triggeredByMe = False+ } where cidInfo = CIDInfo 0 cid (StatelessResetToken "") ---------------------------------------------------------------- -data MigrationState = NonMigration- | MigrationStarted- | SendChallenge [PathData]- | RecvResponse- deriving (Eq, Show)+data MigrationState+ = NonMigration+ | MigrationStarted+ | SendChallenge [PathData]+ | RecvResponse+ deriving (Eq, Show) ---------------------------------------------------------------- -data Coder = Coder {- encrypt :: Buffer -> PlainText -> AssDat -> PacketNumber -> IO Int- , decrypt :: Buffer -> CipherText -> AssDat -> PacketNumber -> IO Int- , supplement :: Maybe Supplement- }+data Coder = Coder+ { encrypt :: Buffer -> PlainText -> AssDat -> PacketNumber -> IO Int+ , decrypt :: Buffer -> CipherText -> AssDat -> PacketNumber -> IO Int+ , supplement :: Maybe Supplement+ } initialCoder :: Coder-initialCoder = Coder {- encrypt = \_ _ _ _ -> return (-1)- , decrypt = \_ _ _ _ -> return (-1)- , supplement = Nothing- }+initialCoder =+ Coder+ { encrypt = \_ _ _ _ -> return (-1)+ , decrypt = \_ _ _ _ -> return (-1)+ , supplement = Nothing+ } -data Coder1RTT = Coder1RTT {- coder1RTT :: Coder- , secretN :: TrafficSecrets ApplicationSecret- }+data Coder1RTT = Coder1RTT+ { coder1RTT :: Coder+ , secretN :: TrafficSecrets ApplicationSecret+ } initialCoder1RTT :: Coder1RTT-initialCoder1RTT = Coder1RTT {- coder1RTT = initialCoder- , secretN = (ClientTrafficSecret "", ServerTrafficSecret "")- }+initialCoder1RTT =+ Coder1RTT+ { coder1RTT = initialCoder+ , secretN = (ClientTrafficSecret "", ServerTrafficSecret "")+ } -data Protector = Protector {- setSample :: Buffer -> IO ()- , getMask :: IO Buffer- , unprotect :: Sample -> Mask- }+data Protector = Protector+ { setSample :: Buffer -> IO ()+ , getMask :: IO Buffer+ , unprotect :: Sample -> Mask+ } initialProtector :: Protector-initialProtector = Protector {- setSample = \_ -> return ()- , getMask = return nullPtr- , unprotect = \_ -> Mask ""- }+initialProtector =+ Protector+ { setSample = \_ -> return ()+ , getMask = return nullPtr+ , unprotect = \_ -> Mask ""+ } ---------------------------------------------------------------- -data Negotiated = Negotiated {- tlsHandshakeMode :: HandshakeMode13+data Negotiated = Negotiated+ { tlsHandshakeMode :: HandshakeMode13 , applicationProtocol :: Maybe NegotiatedProtocol , applicationSecretInfo :: ApplicationSecretInfo } initialNegotiated :: Negotiated-initialNegotiated = Negotiated {- tlsHandshakeMode = FullHandshake- , applicationProtocol = Nothing- , applicationSecretInfo = ApplicationSecretInfo defaultTrafficSecrets- }+initialNegotiated =+ Negotiated+ { tlsHandshakeMode = FullHandshake+ , applicationProtocol = Nothing+ , applicationSecretInfo = ApplicationSecretInfo defaultTrafficSecrets+ } ---------------------------------------------------------------- -newtype StreamIdBase = StreamIdBase { fromStreamIdBase :: Int }+newtype StreamIdBase = StreamIdBase {fromStreamIdBase :: Int} deriving (Eq, Show) -data Concurrency = Concurrency {- currentStream :: StreamId- , maxStreams :: StreamIdBase- }+data Concurrency = Concurrency+ { currentStream :: StreamId+ , maxStreams :: StreamIdBase+ }+ deriving (Show) newConcurrency :: Role -> Direction -> Int -> Concurrency newConcurrency rl dir n = Concurrency ini $ StreamIdBase n- where- bidi = dir == Bidirectional- ini | rl == Client = if bidi then 0 else 2- | otherwise = if bidi then 1 else 3+ where+ bidi = dir == Bidirectional+ ini+ | rl == Client = if bidi then 0 else 2+ | otherwise = if bidi then 1 else 3 ---------------------------------------------------------------- @@ -174,81 +189,82 @@ ---------------------------------------------------------------- -- | A quic connection to carry multiple streams.-data Connection = Connection {- connState :: ConnState- -- Actions- , connDebugLog :: DebugLogger -- ^ A logger for debugging.- , connQLog :: QLogger- , connHooks :: Hooks- , connSend :: ~Send -- ~ for testing- , connRecv :: ~Recv -- ~ for testing- -- Manage- , connRecvQ :: RecvQ- , udpSocket :: ~(IORef UDPSocket)- , readers :: IORef (IO ())- , mainThreadId :: ThreadId- -- Info- , roleInfo :: IORef RoleInfo- , quicVersionInfo :: IORef VersionInfo- , origVersionInfo :: VersionInfo -- chosenVersion is client's ver in Initial- -- Mine- , myParameters :: Parameters- , myCIDDB :: IORef CIDDB- -- Peer- , peerParameters :: IORef Parameters- , peerCIDDB :: TVar CIDDB- -- Queues- , inputQ :: InputQ- , cryptoQ :: CryptoQ- , outputQ :: OutputQ- , migrationQ :: MigrationQ- , shared :: Shared- , delayedAckCount :: IORef Int- , delayedAckCancel :: IORef (IO ())- -- State- , peerPacketNumber :: IORef PacketNumber -- for RTT1- , streamTable :: IORef StreamTable- , myStreamId :: TVar Concurrency -- C:0 S:1- , myUniStreamId :: TVar Concurrency -- C:2 S:3- , peerStreamId :: IORef Concurrency -- C:1 S:0- , peerUniStreamId :: IORef Concurrency -- C:3 S:2- , flowTx :: TVar Flow- , flowRx :: IORef Flow- , flowBytesRx :: IORef Int- , migrationState :: TVar MigrationState- , minIdleTimeout :: IORef Microseconds- , bytesTx :: TVar Int -- TVar for anti amplification- , bytesRx :: TVar Int -- TVar for anti amplification- , addressValidated :: TVar Bool- -- TLS- , pendingQ :: Array EncryptionLevel (TVar [ReceivedPacket])- , ciphers :: IOArray EncryptionLevel Cipher- , coders :: IOArray EncryptionLevel Coder- , coders1RTT :: IOArray Bool Coder1RTT- , protectors :: IOArray EncryptionLevel Protector- , currentKeyPhase :: IORef (Bool, PacketNumber)- , negotiated :: IORef Negotiated- , connMyAuthCIDs :: IORef AuthCIDs- , connPeerAuthCIDs :: IORef AuthCIDs- -- Resources- , connResources :: IORef (IO ())- , encodeBuf :: Buffer- , encryptRes :: SizedBuffer- , decryptBuf :: Buffer- -- Recovery- , connLDCC :: LDCC- }+data Connection = Connection+ { connState :: ConnState+ , -- Actions+ connDebugLog :: DebugLogger+ -- ^ A logger for debugging.+ , connQLog :: QLogger+ , connHooks :: Hooks+ , connSend :: ~Send -- ~ for testing+ , connRecv :: ~Recv -- ~ for testing+ -- Manage+ , connRecvQ :: RecvQ+ , udpSocket :: ~(IORef UDPSocket)+ , readers :: IORef (IO ())+ , mainThreadId :: ThreadId+ , controlRate :: Rate+ , -- Info+ roleInfo :: IORef RoleInfo+ , quicVersionInfo :: IORef VersionInfo+ , origVersionInfo :: VersionInfo -- chosenVersion is client's ver in Initial+ -- Mine+ , myParameters :: Parameters+ , myCIDDB :: IORef CIDDB+ , -- Peer+ peerParameters :: IORef Parameters+ , peerCIDDB :: TVar CIDDB+ , -- Queues+ inputQ :: InputQ+ , cryptoQ :: CryptoQ+ , outputQ :: OutputQ+ , migrationQ :: MigrationQ+ , shared :: Shared+ , delayedAckCount :: IORef Int+ , delayedAckCancel :: IORef (IO ())+ , -- State+ peerPacketNumber :: IORef PacketNumber -- for RTT1+ , streamTable :: IORef StreamTable+ , myStreamId :: TVar Concurrency -- C:0 S:1+ , myUniStreamId :: TVar Concurrency -- C:2 S:3+ , peerStreamId :: IORef Concurrency -- C:1 S:0+ , peerUniStreamId :: IORef Concurrency -- C:3 S:2+ , flowTx :: TVar TxFlow+ , flowRx :: IORef RxFlow+ , migrationState :: TVar MigrationState+ , minIdleTimeout :: IORef Microseconds+ , bytesTx :: TVar Int -- TVar for anti amplification+ , bytesRx :: TVar Int -- TVar for anti amplification+ , addressValidated :: TVar Bool+ , -- TLS+ pendingQ :: Array EncryptionLevel (TVar [ReceivedPacket])+ , ciphers :: IOArray EncryptionLevel Cipher+ , coders :: IOArray EncryptionLevel Coder+ , coders1RTT :: IOArray Bool Coder1RTT+ , protectors :: IOArray EncryptionLevel Protector+ , currentKeyPhase :: IORef (Bool, PacketNumber)+ , negotiated :: IORef Negotiated+ , connMyAuthCIDs :: IORef AuthCIDs+ , connPeerAuthCIDs :: IORef AuthCIDs+ , -- Resources+ connResources :: IORef (IO ())+ , encodeBuf :: Buffer+ , encryptRes :: SizedBuffer+ , decryptBuf :: Buffer+ , -- Recovery+ connLDCC :: LDCC+ } instance KeepQlog Connection where keepQlog conn = connQLog conn instance Connector Connection where- getRole = role . connState+ getRole = role . connState getEncryptionLevel = readTVarIO . encryptionLevel . connState- getMaxPacketSize = readIORef . maxPacketSize . connState+ getMaxPacketSize = readIORef . maxPacketSize . connState getConnectionState = readTVarIO . connectionState . connState- getPacketNumber = readIORef . packetNumber . connState- getAlive = readIORef . connectionAlive . connState+ getPacketNumber = readIORef . packetNumber . connState+ getAlive = readIORef . connectionAlive . connState setDead :: Connection -> IO () setDead conn = writeIORef (connectionAlive $ connState conn) False@@ -258,30 +274,36 @@ q1 <- newTVarIO [] q2 <- newTVarIO [] q3 <- newTVarIO []- let lst = [(RTT0Level,q1),(HandshakeLevel,q2),(RTT1Level,q3)]- arr = array (RTT0Level,RTT1Level) lst+ let lst = [(RTT0Level, q1), (HandshakeLevel, q2), (RTT1Level, q3)]+ arr = array (RTT0Level, RTT1Level) lst return arr -newConnection :: Role- -> Parameters- -> VersionInfo -> AuthCIDs -> AuthCIDs- -> DebugLogger -> QLogger -> Hooks- -> IORef UDPSocket- -> RecvQ- -> Send- -> Recv- -> IO Connection+newConnection+ :: Role+ -> Parameters+ -> VersionInfo+ -> AuthCIDs+ -> AuthCIDs+ -> DebugLogger+ -> QLogger+ -> Hooks+ -> IORef UDPSocket+ -> RecvQ+ -> Send+ -> Recv+ -> IO Connection newConnection rl myparams verInfo myAuthCIDs peerAuthCIDs debugLog qLog hooks sref recvQ ~send ~recv = do outQ <- newTQueueIO let put x = atomically $ writeTQueue outQ $ OutRetrans x connstate <- newConnState rl let bufsiz = maximumUdpPayloadSize- encBuf <- mallocBytes bufsiz+ encBuf <- mallocBytes bufsiz ecrptBuf <- mallocBytes bufsiz dcrptBuf <- mallocBytes bufsiz Connection connstate debugLog qLog hooks send recv recvQ sref <$> newIORef (return ()) <*> myThreadId+ <*> newRate -- Info <*> newIORef initialRoleInfo <*> newIORef verInfo@@ -303,13 +325,12 @@ -- State <*> newIORef 0 <*> newIORef emptyStreamTable- <*> newTVarIO (newConcurrency rl Bidirectional 0)+ <*> newTVarIO (newConcurrency rl Bidirectional 0) <*> newTVarIO (newConcurrency rl Unidirectional 0)- <*> newIORef peerConcurrency- <*> newIORef peerUniConcurrency- <*> newTVarIO defaultFlow- <*> newIORef defaultFlow { flowMaxData = initialMaxData myparams }- <*> newIORef 0+ <*> newIORef peerConcurrency+ <*> newIORef peerUniConcurrency+ <*> newTVarIO (newTxFlow 0) -- limit is set in Handshake+ <*> newIORef (newRxFlow $ initialMaxData myparams) <*> newTVarIO NonMigration <*> newIORef (milliToMicro $ maxIdleTimeout myparams) <*> newTVarIO 0@@ -317,17 +338,17 @@ <*> newTVarIO False -- TLS <*> makePendingQ- <*> newArray (InitialLevel,RTT1Level) defaultCipher- <*> newArray (InitialLevel,HandshakeLevel) initialCoder- <*> newArray (False,True) initialCoder1RTT- <*> newArray (InitialLevel,RTT1Level) initialProtector- <*> newIORef (False,0)+ <*> newArray (InitialLevel, RTT1Level) defaultCipher+ <*> newArray (InitialLevel, HandshakeLevel) initialCoder+ <*> newArray (False, True) initialCoder1RTT+ <*> newArray (InitialLevel, RTT1Level) initialProtector+ <*> newIORef (False, 0) <*> newIORef initialNegotiated <*> newIORef myAuthCIDs <*> newIORef peerAuthCIDs -- Resources <*> newIORef (free encBuf >> free ecrptBuf >> free dcrptBuf)- <*> return encBuf -- used sender or closere+ <*> return encBuf -- used sender or closere <*> return (SizedBuffer ecrptBuf bufsiz) -- used sender <*> return dcrptBuf -- used receiver -- Recovery@@ -335,12 +356,13 @@ where isclient = rl == Client initialRoleInfo- | isclient = defaultClientRoleInfo- | otherwise = defaultServerRoleInfo- myCID = fromJust $ initSrcCID myAuthCIDs+ | isclient = defaultClientRoleInfo+ | otherwise = defaultServerRoleInfo+ myCID = fromJust $ initSrcCID myAuthCIDs peerCID = fromJust $ initSrcCID peerAuthCIDs- peer | isclient = Server- | otherwise = Client+ peer+ | isclient = Server+ | otherwise = Client peerConcurrency = newConcurrency peer Bidirectional (initialMaxStreamsBidi myparams) peerUniConcurrency = newConcurrency peer Unidirectional (initialMaxStreamsUni myparams) @@ -349,34 +371,49 @@ ---------------------------------------------------------------- -clientConnection :: ClientConfig- -> VersionInfo -> AuthCIDs -> AuthCIDs- -> DebugLogger -> QLogger -> Hooks- -> IORef UDPSocket- -> RecvQ -> Send -> Recv- -> IO Connection+clientConnection+ :: ClientConfig+ -> VersionInfo+ -> AuthCIDs+ -> AuthCIDs+ -> DebugLogger+ -> QLogger+ -> Hooks+ -> IORef UDPSocket+ -> RecvQ+ -> Send+ -> Recv+ -> IO Connection clientConnection ClientConfig{..} verInfo myAuthCIDs peerAuthCIDs = newConnection Client ccParameters verInfo myAuthCIDs peerAuthCIDs -serverConnection :: ServerConfig- -> VersionInfo -> AuthCIDs -> AuthCIDs- -> DebugLogger -> QLogger -> Hooks- -> IORef UDPSocket- -> RecvQ -> Send -> Recv- -> IO Connection+serverConnection+ :: ServerConfig+ -> VersionInfo+ -> AuthCIDs+ -> AuthCIDs+ -> DebugLogger+ -> QLogger+ -> Hooks+ -> IORef UDPSocket+ -> RecvQ+ -> Send+ -> Recv+ -> IO Connection serverConnection ServerConfig{..} verInfo myAuthCIDs peerAuthCIDs = newConnection Server scParameters verInfo myAuthCIDs peerAuthCIDs ---------------------------------------------------------------- -newtype Input = InpStream Stream deriving Show-data Crypto = InpHandshake EncryptionLevel ByteString deriving Show+newtype Input = InpStream Stream deriving (Show)+data Crypto = InpHandshake EncryptionLevel ByteString deriving (Show) -data Output = OutControl EncryptionLevel [Frame] (IO ())- | OutHandshake [(EncryptionLevel,ByteString)]- | OutRetrans PlainPacket+data Output+ = OutControl EncryptionLevel [Frame] (IO ())+ | OutHandshake [(EncryptionLevel, ByteString)]+ | OutRetrans PlainPacket -type InputQ = TQueue Input+type InputQ = TQueue Input type CryptoQ = TQueue Crypto type OutputQ = TQueue Output type MigrationQ = TQueue ReceivedPacket@@ -385,15 +422,17 @@ type SendStreamQ = TQueue TxStreamData -data Shared = Shared {- sharedCloseSent :: IORef Bool- , sharedCloseReceived :: IORef Bool- , shared1RTTReady :: IORef Bool- , sharedSendStreamQ :: SendStreamQ- }+data Shared = Shared+ { sharedCloseSent :: IORef Bool+ , sharedCloseReceived :: IORef Bool+ , shared1RTTReady :: IORef Bool+ , sharedSendStreamQ :: SendStreamQ+ } newShared :: IO Shared-newShared = Shared <$> newIORef False- <*> newIORef False- <*> newIORef False- <*> newTQueueIO+newShared =+ Shared+ <$> newIORef False+ <*> newIORef False+ <*> newIORef False+ <*> newTQueueIO
Network/QUIC/Connector.hs view
@@ -5,34 +5,35 @@ import UnliftIO.STM class Connector a where- getRole :: a -> Role+ getRole :: a -> Role getEncryptionLevel :: a -> IO EncryptionLevel- getMaxPacketSize :: a -> IO Int+ getMaxPacketSize :: a -> IO Int getConnectionState :: a -> IO ConnectionState- getPacketNumber :: a -> IO PacketNumber- getAlive :: a -> IO Bool+ getPacketNumber :: a -> IO PacketNumber+ getAlive :: a -> IO Bool ---------------------------------------------------------------- -data ConnState = ConnState {- role :: Role- , connectionState :: TVar ConnectionState- , packetNumber :: IORef PacketNumber -- squeezing three to one- , encryptionLevel :: TVar EncryptionLevel -- to synchronize- , maxPacketSize :: IORef Int- -- Explicitly separated from 'ConnectionState'- -- It seems that STM triggers a dead-lock if- -- it is used in the close function of bracket.- , connectionAlive :: IORef Bool- }+data ConnState = ConnState+ { role :: Role+ , connectionState :: TVar ConnectionState+ , packetNumber :: IORef PacketNumber -- squeezing three to one+ , encryptionLevel :: TVar EncryptionLevel -- to synchronize+ , maxPacketSize :: IORef Int+ , -- Explicitly separated from 'ConnectionState'+ -- It seems that STM triggers a dead-lock if+ -- it is used in the close function of bracket.+ connectionAlive :: IORef Bool+ } newConnState :: Role -> IO ConnState newConnState rl =- ConnState rl <$> newTVarIO Handshaking- <*> newIORef 0- <*> newTVarIO InitialLevel- <*> newIORef defaultQUICPacketSize- <*> newIORef True+ ConnState rl+ <$> newTVarIO Handshaking+ <*> newIORef 0+ <*> newTVarIO InitialLevel+ <*> newIORef defaultQUICPacketSize+ <*> newIORef True ---------------------------------------------------------------- @@ -46,15 +47,16 @@ ---------------------------------------------------------------- -data ConnectionState = Handshaking- | ReadyFor0RTT- | ReadyFor1RTT- | Established- deriving (Eq, Ord, Show)+data ConnectionState+ = Handshaking+ | ReadyFor0RTT+ | ReadyFor1RTT+ | Established+ deriving (Eq, Ord, Show) isConnectionEstablished :: Connector a => a -> IO Bool isConnectionEstablished conn = do st <- getConnectionState conn return $ case st of- Established -> True- _ -> False+ Established -> True+ _ -> False
Network/QUIC/Crypto.hs view
@@ -2,12 +2,12 @@ {-# LANGUAGE OverloadedStrings #-} module Network.QUIC.Crypto (- module Network.QUIC.Crypto.Fusion- , module Network.QUIC.Crypto.Nite- , module Network.QUIC.Crypto.Types- , module Network.QUIC.Crypto.Keys- , module Network.QUIC.Crypto.Utils- ) where+ module Network.QUIC.Crypto.Fusion,+ module Network.QUIC.Crypto.Nite,+ module Network.QUIC.Crypto.Types,+ module Network.QUIC.Crypto.Keys,+ module Network.QUIC.Crypto.Utils,+) where import Network.TLS hiding (Version) import Network.TLS.QUIC
Network/QUIC/Crypto/Fusion.hs view
@@ -1,17 +1,17 @@ {-# LANGUAGE CPP #-} module Network.QUIC.Crypto.Fusion (- FusionContext- , fusionNewContext- , fusionSetup- , fusionEncrypt- , fusionDecrypt- , Supplement- , fusionSetupSupplement- , fusionSetSample- , fusionGetMask- , isFusionAvailable- ) where+ FusionContext,+ fusionNewContext,+ fusionSetup,+ fusionEncrypt,+ fusionDecrypt,+ Supplement,+ fusionSetupSupplement,+ fusionSetSample,+ fusionGetMask,+ isFusionAvailable,+) where #ifdef USE_FUSION import qualified Data.ByteString as BS
Network/QUIC/Crypto/Keys.hs view
@@ -2,15 +2,15 @@ {-# LANGUAGE OverloadedStrings #-} module Network.QUIC.Crypto.Keys (- defaultCipher- , initialSecrets- , clientInitialSecret- , serverInitialSecret- , aeadKey- , initialVector- , nextSecret- , headerProtectionKey- ) where+ defaultCipher,+ initialSecrets,+ clientInitialSecret,+ serverInitialSecret,+ aeadKey,+ initialVector,+ nextSecret,+ headerProtectionKey,+) where import Network.TLS hiding (Version) import Network.TLS.Extra.Cipher@@ -29,9 +29,12 @@ ---------------------------------------------------------------- initialSalt :: Version -> Salt-initialSalt Draft29 = "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90\xa8\x99"-initialSalt Version1 = "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a"-initialSalt Version2 = "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb\xf9\xbd\x2e\xd9"+initialSalt Draft29 =+ "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90\xa8\x99"+initialSalt Version1 =+ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a"+initialSalt Version2 =+ "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb\xf9\xbd\x2e\xd9" initialSalt (Version v) = E.impureThrow $ VersionIsUnknown v initialSecrets :: Version -> CID -> TrafficSecrets InitialSecret@@ -44,65 +47,65 @@ serverInitialSecret v c = ServerTrafficSecret $ initialSecret v c $ Label "server in" initialSecret :: Version -> CID -> Label -> ByteString-initialSecret Draft29 = initialSecret' $ initialSalt Draft29+initialSecret Draft29 = initialSecret' $ initialSalt Draft29 initialSecret Version1 = initialSecret' $ initialSalt Version1 initialSecret Version2 = initialSecret' $ initialSalt Version2-initialSecret _ = \_ _ -> "not supported"+initialSecret _ = \_ _ -> "not supported" initialSecret' :: ByteString -> CID -> Label -> ByteString initialSecret' salt cid (Label label) = secret where- cipher = defaultCipher- hash = cipherHash cipher+ cipher = defaultCipher+ hash = cipherHash cipher iniSecret = hkdfExtract hash salt $ fromCID cid- hashSize = hashDigestSize hash- secret = hkdfExpandLabel hash iniSecret label "" hashSize+ hashSize = hashDigestSize hash+ secret = hkdfExpandLabel hash iniSecret label "" hashSize aeadKey :: Version -> Cipher -> Secret -> Key-aeadKey Draft29 = genKey $ Label "quic key"+aeadKey Draft29 = genKey $ Label "quic key" aeadKey Version1 = genKey $ Label "quic key" aeadKey Version2 = genKey $ Label "quicv2 key"-aeadKey _ = genKey $ Label "not supported"+aeadKey _ = genKey $ Label "not supported" headerProtectionKey :: Version -> Cipher -> Secret -> Key-headerProtectionKey Draft29 = genKey $ Label "quic hp"+headerProtectionKey Draft29 = genKey $ Label "quic hp" headerProtectionKey Version1 = genKey $ Label "quic hp" headerProtectionKey Version2 = genKey $ Label "quicv2 hp"-headerProtectionKey _ = genKey $ Label "not supported"+headerProtectionKey _ = genKey $ Label "not supported" genKey :: Label -> Cipher -> Secret -> Key genKey (Label label) cipher (Secret secret) = Key key where- hash = cipherHash cipher- bulk = cipherBulk cipher+ hash = cipherHash cipher+ bulk = cipherBulk cipher keySize = bulkKeySize bulk- key = hkdfExpandLabel hash secret label "" keySize+ key = hkdfExpandLabel hash secret label "" keySize initialVector :: Version -> Cipher -> Secret -> IV initialVector ver cipher (Secret secret) = IV iv where- label = ivLabel ver- hash = cipherHash cipher- bulk = cipherBulk cipher+ label = ivLabel ver+ hash = cipherHash cipher+ bulk = cipherBulk cipher ivSize = max 8 (bulkIVSize bulk + bulkExplicitIV bulk)- iv = hkdfExpandLabel hash secret label "" ivSize+ iv = hkdfExpandLabel hash secret label "" ivSize ivLabel :: Version -> ByteString-ivLabel Draft29 = "quic iv"+ivLabel Draft29 = "quic iv" ivLabel Version1 = "quic iv" ivLabel Version2 = "quicv2 iv"-ivLabel _ = "not supported"+ivLabel _ = "not supported" nextSecret :: Version -> Cipher -> Secret -> Secret nextSecret ver cipher (Secret secN) = Secret secN1 where- label = kuLabel ver- hash = cipherHash cipher+ label = kuLabel ver+ hash = cipherHash cipher hashSize = hashDigestSize hash- secN1 = hkdfExpandLabel hash secN label "" hashSize+ secN1 = hkdfExpandLabel hash secN label "" hashSize kuLabel :: Version -> ByteString-kuLabel Draft29 = "quic ku"+kuLabel Draft29 = "quic ku" kuLabel Version1 = "quic ku" kuLabel Version2 = "quicv2 ku"-kuLabel _ = "not supported"+kuLabel _ = "not supported"
Network/QUIC/Crypto/Nite.hs view
@@ -2,17 +2,17 @@ {-# LANGUAGE ScopedTypeVariables #-} module Network.QUIC.Crypto.Nite (- niteEncrypt- , niteEncrypt'- , niteDecrypt- , niteDecrypt'- , protectionMask- , aes128gcmEncrypt- , makeNonce- , makeNiteEncrypt- , makeNiteDecrypt- , makeNiteProtector- ) where+ niteEncrypt,+ niteEncrypt',+ niteDecrypt,+ niteDecrypt',+ protectionMask,+ aes128gcmEncrypt,+ makeNonce,+ makeNiteEncrypt,+ makeNiteDecrypt,+ makeNiteProtector,+) where import Crypto.Cipher.AES import Crypto.Cipher.Types hiding (Cipher, IV)@@ -20,10 +20,10 @@ import qualified Data.ByteArray as Byte (convert) import qualified Data.ByteString as BS import qualified Data.ByteString.Internal as BS-import Foreign.ForeignPtr (withForeignPtr, newForeignPtr_)+import Foreign.ForeignPtr (newForeignPtr_, withForeignPtr) import Foreign.Marshal.Alloc (mallocBytes) import Foreign.Marshal.Utils (copyBytes)-import Foreign.Ptr (Ptr, plusPtr, nullPtr)+import Foreign.Ptr (Ptr, nullPtr, plusPtr) import Foreign.Storable (peek, poke) import Network.TLS hiding (Version) import Network.TLS.Extra.Cipher@@ -37,64 +37,70 @@ -- It would be nice to take [PlainText] and update AEAD context with -- [PlainText]. But since each PlainText is not aligned to cipher block, -- it's impossible.-cipherEncrypt :: Cipher -> Key -> Nonce -> PlainText -> AssDat -> Maybe (CipherText,CipherText)+cipherEncrypt+ :: Cipher -> Key -> Nonce -> PlainText -> AssDat -> Maybe (CipherText, CipherText) cipherEncrypt cipher- | cipher == cipher_TLS13_AES128GCM_SHA256 = aes128gcmEncrypt- | cipher == cipher_TLS13_AES128CCM_SHA256 = error "cipher_TLS13_AES128CCM_SHA256"- | cipher == cipher_TLS13_AES256GCM_SHA384 = aes256gcmEncrypt- | otherwise = error "cipherEncrypt"+ | cipher == cipher_TLS13_AES128GCM_SHA256 = aes128gcmEncrypt+ | cipher == cipher_TLS13_AES128CCM_SHA256 =+ error "cipher_TLS13_AES128CCM_SHA256"+ | cipher == cipher_TLS13_AES256GCM_SHA384 = aes256gcmEncrypt+ | otherwise = error "cipherEncrypt" -cipherDecrypt :: Cipher -> Key -> Nonce -> CipherText -> AssDat -> Maybe PlainText+cipherDecrypt+ :: Cipher -> Key -> Nonce -> CipherText -> AssDat -> Maybe PlainText cipherDecrypt cipher- | cipher == cipher_TLS13_AES128GCM_SHA256 = aes128gcmDecrypt- | cipher == cipher_TLS13_AES128CCM_SHA256 = error "cipher_TLS13_AES128CCM_SHA256"- | cipher == cipher_TLS13_AES256GCM_SHA384 = aes256gcmDecrypt- | otherwise = error "cipherDecrypt"+ | cipher == cipher_TLS13_AES128GCM_SHA256 = aes128gcmDecrypt+ | cipher == cipher_TLS13_AES128CCM_SHA256 =+ error "cipher_TLS13_AES128CCM_SHA256"+ | cipher == cipher_TLS13_AES256GCM_SHA384 = aes256gcmDecrypt+ | otherwise = error "cipherDecrypt" -- IMPORTANT: Using 'let' so that parameters can be memorized.-aes128gcmEncrypt :: Key -> (Nonce -> PlainText -> AssDat -> Maybe (CipherText,CipherText))+aes128gcmEncrypt+ :: Key -> (Nonce -> PlainText -> AssDat -> Maybe (CipherText, CipherText)) aes128gcmEncrypt (Key key) = case maybeCryptoError $ cipherInit key of- Nothing -> \_ _ _ -> Nothing- Just (aes :: AES128) -> \(Nonce nonce) plaintext (AssDat ad) ->- case maybeCryptoError $ aeadInit AEAD_GCM aes nonce of- Nothing -> Nothing- Just aead ->- let (AuthTag tag0, ciphertext) = aeadSimpleEncrypt aead ad plaintext 16- tag = Byte.convert tag0- in Just (ciphertext,tag)+ Nothing -> \_ _ _ -> Nothing+ Just (aes :: AES128) -> \(Nonce nonce) plaintext (AssDat ad) ->+ case maybeCryptoError $ aeadInit AEAD_GCM aes nonce of+ Nothing -> Nothing+ Just aead ->+ let (AuthTag tag0, ciphertext) = aeadSimpleEncrypt aead ad plaintext 16+ tag = Byte.convert tag0+ in Just (ciphertext, tag) aes128gcmDecrypt :: Key -> (Nonce -> CipherText -> AssDat -> Maybe PlainText) aes128gcmDecrypt (Key key) = case maybeCryptoError $ cipherInit key of- Nothing -> \_ _ _ -> Nothing- Just (aes :: AES128) -> \(Nonce nonce) ciphertag (AssDat ad) ->- case maybeCryptoError $ aeadInit AEAD_GCM aes nonce of- Nothing -> Nothing- Just aead ->- let (ciphertext, tag) = BS.splitAt (BS.length ciphertag - 16) ciphertag- authtag = AuthTag $ Byte.convert tag- in aeadSimpleDecrypt aead ad ciphertext authtag+ Nothing -> \_ _ _ -> Nothing+ Just (aes :: AES128) -> \(Nonce nonce) ciphertag (AssDat ad) ->+ case maybeCryptoError $ aeadInit AEAD_GCM aes nonce of+ Nothing -> Nothing+ Just aead ->+ let (ciphertext, tag) = BS.splitAt (BS.length ciphertag - 16) ciphertag+ authtag = AuthTag $ Byte.convert tag+ in aeadSimpleDecrypt aead ad ciphertext authtag -aes256gcmEncrypt :: Key -> (Nonce -> PlainText -> AssDat -> Maybe (CipherText,CipherText))+aes256gcmEncrypt+ :: Key -> (Nonce -> PlainText -> AssDat -> Maybe (CipherText, CipherText)) aes256gcmEncrypt (Key key) = case maybeCryptoError $ cipherInit key of- Nothing -> \_ _ _ -> Nothing- Just (aes :: AES256) -> \(Nonce nonce) plaintext (AssDat ad) ->- case maybeCryptoError $ aeadInit AEAD_GCM aes nonce of- Nothing -> Nothing- Just aead ->- let (AuthTag tag0, ciphertext) = aeadSimpleEncrypt aead ad plaintext 16- tag = Byte.convert tag0- in Just (ciphertext,tag)+ Nothing -> \_ _ _ -> Nothing+ Just (aes :: AES256) -> \(Nonce nonce) plaintext (AssDat ad) ->+ case maybeCryptoError $ aeadInit AEAD_GCM aes nonce of+ Nothing -> Nothing+ Just aead ->+ let (AuthTag tag0, ciphertext) = aeadSimpleEncrypt aead ad plaintext 16+ tag = Byte.convert tag0+ in Just (ciphertext, tag) aes256gcmDecrypt :: Key -> (Nonce -> CipherText -> AssDat -> Maybe PlainText) aes256gcmDecrypt (Key key) = case maybeCryptoError $ cipherInit key of- Nothing -> \_ _ _ -> Nothing- Just (aes :: AES256) -> \(Nonce nonce) ciphertag (AssDat ad) ->- case maybeCryptoError $ aeadInit AEAD_GCM aes nonce of- Nothing -> Nothing- Just aead ->- let (ciphertext, tag) = BS.splitAt (BS.length ciphertag - 16) ciphertag- authtag = AuthTag $ Byte.convert tag- in aeadSimpleDecrypt aead ad ciphertext authtag+ Nothing -> \_ _ _ -> Nothing+ Just (aes :: AES256) -> \(Nonce nonce) ciphertag (AssDat ad) ->+ case maybeCryptoError $ aeadInit AEAD_GCM aes nonce of+ Nothing -> Nothing+ Just aead ->+ let (ciphertext, tag) = BS.splitAt (BS.length ciphertag - 16) ciphertag+ authtag = AuthTag $ Byte.convert tag+ in aeadSimpleDecrypt aead ad ciphertext authtag ---------------------------------------------------------------- @@ -112,15 +118,15 @@ -- Nonce +IIIIIIIIIIIIIIIIII--------+ bsXORpad :: ByteString -> ByteString -> ByteString bsXORpad (PS fp0 off0 len0) (PS fp1 off1 len1)- | len0 < len1 = error "bsXORpad"- | otherwise = BS.unsafeCreate len0 $ \dst ->- withForeignPtr fp0 $ \p0 ->- withForeignPtr fp1 $ \p1 -> do- let src0 = p0 `plusPtr` off0- let src1 = p1 `plusPtr` off1- let diff = len0 - len1- copyBytes dst src0 diff- loop (dst `plusPtr` diff) (src0 `plusPtr` diff) src1 len1+ | len0 < len1 = error "bsXORpad"+ | otherwise = BS.unsafeCreate len0 $ \dst ->+ withForeignPtr fp0 $ \p0 ->+ withForeignPtr fp1 $ \p1 -> do+ let src0 = p0 `plusPtr` off0+ let src1 = p1 `plusPtr` off1+ let diff = len0 - len1+ copyBytes dst src0 diff+ loop (dst `plusPtr` diff) (src0 `plusPtr` diff) src1 len1 where loop :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Int -> IO () loop _ _ _ 0 = return ()@@ -146,25 +152,35 @@ makeNiteEncrypt :: Cipher -> Key -> IV -> NiteEncrypt makeNiteEncrypt cipher key iv = niteEncryptWrapper (niteEncrypt cipher key iv) -niteEncryptWrapper :: (PlainText -> AssDat -> PacketNumber -> Maybe (CipherText,CipherText)) -> NiteEncrypt+niteEncryptWrapper+ :: (PlainText -> AssDat -> PacketNumber -> Maybe (CipherText, CipherText))+ -> NiteEncrypt niteEncryptWrapper enc dst plaintext ad pn = case enc plaintext ad pn of Nothing -> return (-1)- Just (hdr,bdy) -> do+ Just (hdr, bdy) -> do len <- copyBS dst hdr let dst' = dst `plusPtr` len len' <- copyBS dst' bdy return (len + len') -niteEncrypt :: Cipher -> Key -> IV- -> PlainText -> AssDat -> PacketNumber -> Maybe (CipherText,CipherText)+niteEncrypt+ :: Cipher+ -> Key+ -> IV+ -> PlainText+ -> AssDat+ -> PacketNumber+ -> Maybe (CipherText, CipherText) niteEncrypt cipher key iv = let enc = cipherEncrypt cipher key- mk = makeNonce iv- in \plaintext header pn -> let bytePN = bytestring64 $ fromIntegral pn- nonce = mk bytePN- in enc nonce plaintext header+ mk = makeNonce iv+ in \plaintext header pn ->+ let bytePN = bytestring64 $ fromIntegral pn+ nonce = mk bytePN+ in enc nonce plaintext header -niteEncrypt' :: Cipher -> Key -> Nonce -> PlainText -> AssDat -> Maybe (CipherText,CipherText)+niteEncrypt'+ :: Cipher -> Key -> Nonce -> PlainText -> AssDat -> Maybe (CipherText, CipherText) niteEncrypt' cipher key nonce plaintext header = cipherEncrypt cipher key nonce plaintext header @@ -175,21 +191,30 @@ makeNiteDecrypt :: Cipher -> Key -> IV -> NiteDecrypt makeNiteDecrypt cipher key iv = niteDecryptWrapper (niteDecrypt cipher key iv) -niteDecryptWrapper :: (CipherText -> AssDat -> PacketNumber -> Maybe PlainText) -> NiteDecrypt+niteDecryptWrapper+ :: (CipherText -> AssDat -> PacketNumber -> Maybe PlainText) -> NiteDecrypt niteDecryptWrapper dec dst ciphertext ad pn = case dec ciphertext ad pn of- Nothing -> return (-1)- Just bs -> copyBS dst bs+ Nothing -> return (-1)+ Just bs -> copyBS dst bs -niteDecrypt :: Cipher -> Key -> IV- -> CipherText -> AssDat -> PacketNumber -> Maybe PlainText+niteDecrypt+ :: Cipher+ -> Key+ -> IV+ -> CipherText+ -> AssDat+ -> PacketNumber+ -> Maybe PlainText niteDecrypt cipher key iv = let dec = cipherDecrypt cipher key- mk = makeNonce iv- in \ciphertext header pn -> let bytePN = bytestring64 (fromIntegral pn)- nonce = mk bytePN- in dec nonce ciphertext header+ mk = makeNonce iv+ in \ciphertext header pn ->+ let bytePN = bytestring64 (fromIntegral pn)+ nonce = mk bytePN+ in dec nonce ciphertext header -niteDecrypt' :: Cipher -> Key -> Nonce -> CipherText -> AssDat -> Maybe PlainText+niteDecrypt'+ :: Cipher -> Key -> Nonce -> CipherText -> AssDat -> Maybe PlainText niteDecrypt' cipher key nonce ciphertext header = cipherDecrypt cipher key nonce ciphertext header @@ -198,30 +223,34 @@ protectionMask :: Cipher -> Key -> (Sample -> Mask) protectionMask cipher key = let f = cipherHeaderProtection cipher key- in \sample -> f sample+ in \sample -> f sample cipherHeaderProtection :: Cipher -> Key -> (Sample -> Mask) cipherHeaderProtection cipher key- | cipher == cipher_TLS13_AES128GCM_SHA256 = aes128ecbEncrypt key- | cipher == cipher_TLS13_AES128CCM_SHA256 = error "cipher_TLS13_AES128CCM_SHA256"- | cipher == cipher_TLS13_AES256GCM_SHA384 = aes256ecbEncrypt key- | otherwise = error "cipherHeaderProtection"+ | cipher == cipher_TLS13_AES128GCM_SHA256 = aes128ecbEncrypt key+ | cipher == cipher_TLS13_AES128CCM_SHA256 =+ error "cipher_TLS13_AES128CCM_SHA256"+ | cipher == cipher_TLS13_AES256GCM_SHA384 = aes256ecbEncrypt key+ | otherwise =+ error "cipherHeaderProtection" aes128ecbEncrypt :: Key -> (Sample -> Mask) aes128ecbEncrypt (Key key) = case maybeCryptoError $ cipherInit key of- Nothing -> \_ -> Mask "0123456789012345"- Just (aes :: AES128) ->- let encrypt = ecbEncrypt aes- in \(Sample sample) -> let mask = encrypt sample- in Mask mask+ Nothing -> \_ -> Mask "0123456789012345"+ Just (aes :: AES128) ->+ let encrypt = ecbEncrypt aes+ in \(Sample sample) ->+ let mask = encrypt sample+ in Mask mask aes256ecbEncrypt :: Key -> (Sample -> Mask) aes256ecbEncrypt (Key key) = case maybeCryptoError $ cipherInit key of- Nothing -> \_ -> Mask "0123456789012345"- Just (aes :: AES256) ->- let encrypt = ecbEncrypt aes- in \(Sample sample) -> let mask = encrypt sample- in Mask mask+ Nothing -> \_ -> Mask "0123456789012345"+ Just (aes :: AES256) ->+ let encrypt = ecbEncrypt aes+ in \(Sample sample) ->+ let mask = encrypt sample+ in Mask mask ----------------------------------------------------------------
Network/QUIC/Crypto/Types.hs view
@@ -1,23 +1,23 @@ module Network.QUIC.Crypto.Types (- -- * Types- PlainText- , CipherText- , Key(..)- , IV(..)- , CID- , Secret(..)- , AssDat(..)- , Sample(..)- , Mask(..)- , Nonce(..)- , Salt- , Label(..)- , Cipher- , InitialSecret- , TrafficSecrets- , ClientTrafficSecret(..)- , ServerTrafficSecret(..)- ) where+ -- * Types+ PlainText,+ CipherText,+ Key (..),+ IV (..),+ CID,+ Secret (..),+ AssDat (..),+ Sample (..),+ Mask (..),+ Nonce (..),+ Salt,+ Label (..),+ Cipher,+ InitialSecret,+ TrafficSecrets,+ ClientTrafficSecret (..),+ ServerTrafficSecret (..),+) where import qualified Data.ByteString.Char8 as C8 import Network.TLS hiding (Version)@@ -28,18 +28,18 @@ ---------------------------------------------------------------- -type PlainText = ByteString+type PlainText = ByteString type CipherText = ByteString-type Salt = ByteString+type Salt = ByteString -newtype Key = Key ByteString deriving (Eq)-newtype IV = IV ByteString deriving (Eq)+newtype Key = Key ByteString deriving (Eq)+newtype IV = IV ByteString deriving (Eq) newtype Secret = Secret ByteString deriving (Eq) newtype AssDat = AssDat ByteString deriving (Eq) newtype Sample = Sample ByteString deriving (Eq)-newtype Mask = Mask ByteString deriving (Eq)-newtype Label = Label ByteString deriving (Eq)-newtype Nonce = Nonce ByteString deriving (Eq)+newtype Mask = Mask ByteString deriving (Eq)+newtype Label = Label ByteString deriving (Eq)+newtype Nonce = Nonce ByteString deriving (Eq) instance Show Key where show (Key x) = "Key=" ++ C8.unpack (enc16 x)
Network/QUIC/Crypto/Utils.hs view
@@ -1,11 +1,11 @@ {-# LANGUAGE OverloadedStrings #-} module Network.QUIC.Crypto.Utils (- tagLength- , sampleLength- , bsXOR- , calculateIntegrityTag- ) where+ tagLength,+ sampleLength,+ bsXOR,+ calculateIntegrityTag,+) where import qualified Data.ByteArray as Byte (xor) import qualified Data.ByteString as BS@@ -27,35 +27,38 @@ tagLength :: Cipher -> Int tagLength cipher- | cipher == cipher_TLS13_AES128GCM_SHA256 = 16- | cipher == cipher_TLS13_AES128CCM_SHA256 = 16- | cipher == cipher_TLS13_AES256GCM_SHA384 = 16- | otherwise = error "tagLength"+ | cipher == cipher_TLS13_AES128GCM_SHA256 = 16+ | cipher == cipher_TLS13_AES128CCM_SHA256 = 16+ | cipher == cipher_TLS13_AES256GCM_SHA384 = 16+ | otherwise = error "tagLength" sampleLength :: Cipher -> Int sampleLength cipher- | cipher == cipher_TLS13_AES128GCM_SHA256 = 16- | cipher == cipher_TLS13_AES128CCM_SHA256 = 16- | cipher == cipher_TLS13_AES256GCM_SHA384 = 16- | otherwise = error "sampleLength"+ | cipher == cipher_TLS13_AES128GCM_SHA256 = 16+ | cipher == cipher_TLS13_AES128CCM_SHA256 = 16+ | cipher == cipher_TLS13_AES256GCM_SHA384 = 16+ | otherwise = error "sampleLength" ---------------------------------------------------------------- calculateIntegrityTag :: Version -> CID -> ByteString -> ByteString calculateIntegrityTag ver oCID pseudo0 = case aes128gcmEncrypt (key ver) (nonce ver) "" (AssDat pseudo) of- Nothing -> ""- Just (hdr,bdy) -> hdr `BS.append` bdy+ Nothing -> ""+ Just (hdr, bdy) -> hdr `BS.append` bdy where (ocid, ocidlen) = unpackCID oCID- pseudo = BS.concat [BS.singleton ocidlen- ,Short.fromShort ocid- ,pseudo0]- key Draft29 = Key "\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1"+ pseudo =+ BS.concat+ [ BS.singleton ocidlen+ , Short.fromShort ocid+ , pseudo0+ ]+ key Draft29 = Key "\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1" key Version1 = Key "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e" key Version2 = Key "\x8f\xb4\xb0\x1b\x56\xac\x48\xe2\x60\xfb\xcb\xce\xad\x7c\xcc\x92"- key _ = Key "not supported"- nonce Draft29 = Nonce "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c"+ key _ = Key "not supported"+ nonce Draft29 = Nonce "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c" nonce Version1 = Nonce "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb" nonce Version2 = Nonce "\xd8\x69\x69\xbc\x2d\x7c\x6d\x99\x90\xef\xb0\x4a"- nonce _ = Nonce "not supported"+ nonce _ = Nonce "not supported"
Network/QUIC/Event.hs view
@@ -1,13 +1,14 @@ {-# LANGUAGE CPP #-}+ module Network.QUIC.Event (- getSystemTimerManager- , registerTimeout- , unregisterTimeout- , updateTimeout- , TimerManager- , TimeoutCallback- , TimeoutKey- ) where+ getSystemTimerManager,+ registerTimeout,+ unregisterTimeout,+ updateTimeout,+ TimerManager,+ TimeoutCallback,+ TimeoutKey,+) where #if defined(mingw32_HOST_OS) import GHC.Event.Windows
Network/QUIC/Exception.hs view
@@ -1,9 +1,9 @@ {-# LANGUAGE ScopedTypeVariables #-} module Network.QUIC.Exception (- handleLogT- , handleLogUnit- ) where+ handleLogT,+ handleLogUnit,+) where import qualified GHC.IO.Exception as E import qualified System.IO.Error as E@@ -17,11 +17,11 @@ where handler :: E.SomeException -> IO () handler se = case E.fromException se of- -- threadWait: invalid argument (Bad file descriptor)- Just e | E.ioeGetErrorType e == E.InvalidArgument -> return ()- -- recvBuf: does not exist (Connection refused)- Just e | E.ioeGetErrorType e == E.NoSuchThing -> return ()- _ -> logAction $ bhow se+ -- threadWait: invalid argument (Bad file descriptor)+ Just e | E.ioeGetErrorType e == E.InvalidArgument -> return ()+ -- recvBuf: does not exist (Connection refused)+ Just e | E.ioeGetErrorType e == E.NoSuchThing -> return ()+ _ -> logAction $ bhow se -- Log and throw an exception handleLogT :: DebugLogger -> IO a -> IO a
Network/QUIC/IO.hs view
@@ -3,13 +3,13 @@ module Network.QUIC.IO where import qualified Data.ByteString as BS+import Network.Control import qualified UnliftIO.Exception as E import UnliftIO.STM import Network.QUIC.Connection import Network.QUIC.Connector import Network.QUIC.Imports-import Network.QUIC.Parameters import Network.QUIC.Stream import Network.QUIC.Types @@ -33,10 +33,11 @@ ---------------------------------------------------------------- -data Blocked = BothBlocked Stream Int Int- | ConnBlocked Int- | StrmBlocked Stream Int- deriving Show+data Blocked+ = BothBlocked Stream Int Int+ | ConnBlocked Int+ | StrmBlocked Stream Int+ deriving (Show) addTx :: Connection -> Stream -> Int -> IO () addTx conn s len = atomically $ do@@ -45,19 +46,19 @@ -- | Sending a list of data in the stream. sendStreamMany :: Stream -> [ByteString] -> IO ()-sendStreamMany _ [] = return ()+sendStreamMany _ [] = return () sendStreamMany s dats0 = do sclosed <- isTxStreamClosed s when sclosed $ E.throwIO StreamIsClosed -- fixme: size check for 0RTT let len = totalLen dats0 ready <- isConnection1RTTReady conn- if not ready then do- -- 0-RTT- putSendStreamQ conn $ TxStreamData s dats0 len False- addTx conn s len- else- flowControl dats0 len False+ if not ready+ then do+ -- 0-RTT+ putSendStreamQ conn $ TxStreamData s dats0 len False+ addTx conn s len+ else flowControl dats0 len False where conn = streamConnection s flowControl dats len wait = do@@ -66,36 +67,37 @@ -- FLOW CONTROL: MAX_DATA: send: respecting peer's limit eblocked <- checkBlocked s len wait case eblocked of- Right n- | len == n -> do- putSendStreamQ conn $ TxStreamData s dats len False- addTx conn s n- | otherwise -> do- let (dats1,dats2) = split n dats- putSendStreamQ conn $ TxStreamData s dats1 n False- addTx conn s n- flowControl dats2 (len - n) False- Left blocked -> do- -- fixme: RTT0Level?- sendBlocked conn RTT1Level blocked- flowControl dats len True+ Right n+ | len == n -> do+ putSendStreamQ conn $ TxStreamData s dats len False+ addTx conn s n+ | otherwise -> do+ let (dats1, dats2) = split n dats+ putSendStreamQ conn $ TxStreamData s dats1 n False+ addTx conn s n+ flowControl dats2 (len - n) False+ Left blocked -> do+ -- fixme: RTT0Level?+ sendBlocked conn RTT1Level blocked+ flowControl dats len True sendBlocked :: Connection -> EncryptionLevel -> Blocked -> IO () sendBlocked conn lvl blocked = sendFrames conn lvl frames where frames = case blocked of- StrmBlocked strm n -> [StreamDataBlocked (streamId strm) n]- ConnBlocked n -> [DataBlocked n]- BothBlocked strm n m -> [StreamDataBlocked (streamId strm) n, DataBlocked m]+ StrmBlocked strm n -> [StreamDataBlocked (streamId strm) n]+ ConnBlocked n -> [DataBlocked n]+ BothBlocked strm n m -> [StreamDataBlocked (streamId strm) n, DataBlocked m] -split :: Int -> [BS.ByteString] -> ([BS.ByteString],[BS.ByteString])+split :: Int -> [BS.ByteString] -> ([BS.ByteString], [BS.ByteString]) split n0 dats0 = loop n0 dats0 id where- loop 0 bss build = (build [], bss)- loop _ [] build = (build [], [])- loop n (bs:bss) build = case len `compare` n of- GT -> let (bs1,bs2) = BS.splitAt n bs- in (build [bs1], bs2:bss)+ loop 0 bss build = (build [], bss)+ loop _ [] build = (build [], [])+ loop n (bs : bss) build = case len `compare` n of+ GT ->+ let (bs1, bs2) = BS.splitAt n bs+ in (build [bs1], bs2 : bss) EQ -> (build [bs], bss) LT -> loop (n - len) bss (build . (bs :)) where@@ -106,21 +108,21 @@ let conn = streamConnection s strmFlow <- readStreamFlowTx s connFlow <- readConnectionFlowTx conn- let strmWindow = flowWindow strmFlow- connWindow = flowWindow connFlow+ let strmWindow = txWindowSize strmFlow+ connWindow = txWindowSize connFlow minFlow = min strmWindow connWindow n = min len minFlow when wait $ checkSTM (n > 0)- if n > 0 then- return $ Right n- else do- let cs = len > strmWindow- cw = len > connWindow- blocked- | cs && cw = BothBlocked s (flowMaxData strmFlow) (flowMaxData connFlow)- | cs = StrmBlocked s (flowMaxData strmFlow)- | otherwise = ConnBlocked (flowMaxData connFlow)- return $ Left blocked+ if n > 0+ then return $ Right n+ else do+ let cs = len > strmWindow+ cw = len > connWindow+ blocked+ | cs && cw = BothBlocked s (txfLimit strmFlow) (txfLimit connFlow)+ | cs = StrmBlocked s (txfLimit strmFlow)+ | otherwise = ConnBlocked (txfLimit connFlow)+ return $ Left blocked ---------------------------------------------------------------- @@ -147,23 +149,29 @@ putSendStreamQ conn $ TxStreamData s [] 0 True waitFinTx s delStream conn s- when ((isClient conn && isServerInitiatedBidirectional sid)- || (isServer conn && isClientInitiatedBidirectional sid)) $ do- -- FLOW CONTROL: MAX_STREAMS: recv: announcing my limit properly- checkMaxStreams conn Bidirectional- when ((isClient conn && isServerInitiatedUnidirectional sid)- || (isServer conn && isClientInitiatedUnidirectional sid)) $ do- -- FLOW CONTROL: MAX_STREAMS: recv: announcing my limit properly- checkMaxStreams conn Unidirectional+ when+ ( (isClient conn && isServerInitiatedBidirectional sid)+ || (isServer conn && isClientInitiatedBidirectional sid)+ )+ $ do+ -- FLOW CONTROL: MAX_STREAMS: recv: announcing my limit properly+ checkMaxStreams conn Bidirectional+ when+ ( (isClient conn && isServerInitiatedUnidirectional sid)+ || (isServer conn && isClientInitiatedUnidirectional sid)+ )+ $ do+ -- FLOW CONTROL: MAX_STREAMS: recv: announcing my limit properly+ checkMaxStreams conn Unidirectional where checkMaxStreams conn dir = do mx <- checkStreamIdRoom conn dir case mx of- Nothing -> return ()- Just nms -> do- sendFrames conn RTT1Level [MaxStreams dir nms]- fire conn (Microseconds 50000) $+ Nothing -> return ()+ Just nms -> do sendFrames conn RTT1Level [MaxStreams dir nms]+ fire conn (Microseconds 50000) $+ sendFrames conn RTT1Level [MaxStreams dir nms] -- | Accepting a stream initiated by the peer. acceptStream :: Connection -> IO Stream@@ -177,23 +185,17 @@ recvStream s n = do bs <- takeRecvStreamQwithSize s n let len = BS.length bs+ sid = streamId s conn = streamConnection s- addRxStreamData s len- addRxData conn len- window <- getRxStreamWindow s- let sid = streamId s- initialWindow = initialRxMaxStreamData conn sid -- FLOW CONTROL: MAX_STREAM_DATA: recv: announcing my limit properly- when (window <= (initialWindow !>>. 1)) $ do- newMax <- addRxMaxStreamData s initialWindow+ mxs <- updateStreamFlowRx s len+ forM_ mxs $ \newMax -> do sendFrames conn RTT1Level [MaxStreamData sid newMax] fire conn (Microseconds 50000) $ sendFrames conn RTT1Level [MaxStreamData sid newMax] -- FLOW CONTROL: MAX_DATA: recv: announcing my limit properly- cwindow <- getRxDataWindow conn- let cinitialWindow = initialMaxData $ getMyParameters conn- when (cwindow <= (cinitialWindow !>>. 1)) $ do- newMax <- addRxMaxData conn cinitialWindow+ mxc <- updateFlowRx conn len+ forM_ mxc $ \newMax -> do sendFrames conn RTT1Level [MaxData newMax] fire conn (Microseconds 50000) $ sendFrames conn RTT1Level [MaxData newMax]
Network/QUIC/Info.hs view
@@ -5,8 +5,8 @@ import qualified Data.ByteString.Char8 as C8 import qualified Network.Socket as NS-import Network.TLS hiding (Version, HandshakeFailed)-import Network.UDP (UDPSocket(..))+import Network.TLS hiding (HandshakeFailed, Version)+import Network.UDP (UDPSocket (..)) import Network.QUIC.Connection import Network.QUIC.Imports@@ -15,67 +15,86 @@ ---------------------------------------------------------------- -- | Information about a connection.-data ConnectionInfo = ConnectionInfo {- version :: Version- , cipher :: Cipher- , alpn :: Maybe ByteString- , handshakeMode :: HandshakeMode13- , retry :: Bool- , localSockAddr :: NS.SockAddr- , remoteSockAddr :: NS.SockAddr- , localCID :: CID- , remoteCID :: CID- }+data ConnectionInfo = ConnectionInfo+ { version :: Version+ , cipher :: Cipher+ , alpn :: Maybe ByteString+ , handshakeMode :: HandshakeMode13+ , retry :: Bool+ , localSockAddr :: NS.SockAddr+ , remoteSockAddr :: NS.SockAddr+ , localCID :: CID+ , remoteCID :: CID+ } -- | Getting information about a connection. getConnectionInfo :: Connection -> IO ConnectionInfo getConnectionInfo conn = do UDPSocket{..} <- getSocket conn mysa <- NS.getSocketName udpSocket- mycid <- getMyCID conn+ mycid <- getMyCID conn peercid <- getPeerCID conn c <- getCipher conn RTT1Level mproto <- getApplicationProtocol conn mode <- getTLSMode conn r <- getRetried conn v <- getVersion conn- return ConnectionInfo {- version = v- , cipher = c- , alpn = mproto- , handshakeMode = mode- , retry = r- , localSockAddr = mysa- , remoteSockAddr = peerSockAddr- , localCID = mycid- , remoteCID = peercid- }+ return+ ConnectionInfo+ { version = v+ , cipher = c+ , alpn = mproto+ , handshakeMode = mode+ , retry = r+ , localSockAddr = mysa+ , remoteSockAddr = peerSockAddr+ , localCID = mycid+ , remoteCID = peercid+ } instance Show ConnectionInfo where- show ConnectionInfo{..} = "Version: " ++ show version ++ "\n"- ++ "Cipher: " ++ show cipher ++ "\n"- ++ "ALPN: " ++ maybe "none" C8.unpack alpn ++ "\n"- ++ "Mode: " ++ show handshakeMode ++ "\n"- ++ "Local CID: " ++ show localCID ++ "\n"- ++ "Remote CID: " ++ show remoteCID ++ "\n"- ++ "Local SockAddr: " ++ show localSockAddr ++ "\n"- ++ "Remote SockAddr: " ++ show remoteSockAddr ++- if retry then "\nQUIC retry" else ""+ show ConnectionInfo{..} =+ "Version: "+ ++ show version+ ++ "\n"+ ++ "Cipher: "+ ++ show cipher+ ++ "\n"+ ++ "ALPN: "+ ++ maybe "none" C8.unpack alpn+ ++ "\n"+ ++ "Mode: "+ ++ show handshakeMode+ ++ "\n"+ ++ "Local CID: "+ ++ show localCID+ ++ "\n"+ ++ "Remote CID: "+ ++ show remoteCID+ ++ "\n"+ ++ "Local SockAddr: "+ ++ show localSockAddr+ ++ "\n"+ ++ "Remote SockAddr: "+ ++ show remoteSockAddr+ ++ if retry then "\nQUIC retry" else "" ---------------------------------------------------------------- -- | Statistics of a connection.-data ConnectionStats = ConnectionStats {- txBytes :: Int- , rxBytes :: Int- } deriving (Eq, Show)+data ConnectionStats = ConnectionStats+ { txBytes :: Int+ , rxBytes :: Int+ }+ deriving (Eq, Show) -- | Getting statistics of a connection. getConnectionStats :: Connection -> IO ConnectionStats getConnectionStats conn = do tx <- getTxBytes conn rx <- getRxBytes conn- return $ ConnectionStats {- txBytes = tx- , rxBytes = rx- }+ return $+ ConnectionStats+ { txBytes = tx+ , rxBytes = rx+ }
Network/QUIC/Internal.hs view
@@ -1,22 +1,22 @@ module Network.QUIC.Internal (- module Network.QUIC.Config- , module Network.QUIC.Connection- , module Network.QUIC.Connector- , module Network.QUIC.Crypto- , module Network.QUIC.Logger- , module Network.QUIC.Packet- , module Network.QUIC.Parameters- , module Network.QUIC.Qlog- , module Network.QUIC.Stream- , module Network.QUIC.TLS- , module Network.QUIC.Types- , module Network.QUIC.Utils- , module Network.QUIC.Recovery- , module Network.QUIC.Client.Reader- , module Network.QUIC.Windows- ) where+ module Network.QUIC.Config,+ module Network.QUIC.Connection,+ module Network.QUIC.Connector,+ module Network.QUIC.Crypto,+ module Network.QUIC.Logger,+ module Network.QUIC.Packet,+ module Network.QUIC.Parameters,+ module Network.QUIC.Qlog,+ module Network.QUIC.Stream,+ module Network.QUIC.TLS,+ module Network.QUIC.Types,+ module Network.QUIC.Utils,+ module Network.QUIC.Recovery,+ module Network.QUIC.Client.Reader,+ module Network.QUIC.Windows,+) where -import Network.QUIC.Client.Reader (controlConnection,ConnectionControl(..))+import Network.QUIC.Client.Reader (ConnectionControl (..), controlConnection) import Network.QUIC.Config import Network.QUIC.Connection import Network.QUIC.Connector
Network/QUIC/Logger.hs view
@@ -1,18 +1,18 @@ {-# LANGUAGE OverloadedStrings #-} module Network.QUIC.Logger (- Builder- , DebugLogger- , bhow- , stdoutLogger- , dirDebugLogger- ) where+ Builder,+ DebugLogger,+ bhow,+ stdoutLogger,+ dirDebugLogger,+) where -import System.FilePath-import System.Log.FastLogger import Data.ByteString.Builder (byteString, toLazyByteString) import qualified Data.ByteString.Char8 as C8 import qualified Data.ByteString.Lazy.Char8 as BL+import System.FilePath+import System.Log.FastLogger import Network.QUIC.Imports import Network.QUIC.Types
Network/QUIC/Packet.hs view
@@ -1,39 +1,43 @@ module Network.QUIC.Packet (- -- * Encode- encodeVersionNegotiationPacket- , encodeRetryPacket- , encodePlainPacket- -- * Decode- , decodePacket- , decodePackets- , decodeCryptPackets- , decryptCrypt- , decodeStatelessResetToken- -- * Frame- , encodeFrames- , decodeFramesBuffer- , decodeFramesBS- , countZero -- testing- -- * Header- , isLong- , isShort- , protectFlags- , unprotectFlags- , encodeLongHeaderFlags- , encodeShortHeaderFlags- , decodeLongHeaderPacketType- , encodePktNumLength- , decodePktNumLength- , versionNegotiationPacketType- , retryPacketType- -- * Token- , CryptoToken(..)- , isRetryToken- , generateToken- , generateRetryToken- , encryptToken- , decryptToken- ) where+ -- * Encode+ encodeVersionNegotiationPacket,+ encodeRetryPacket,+ encodePlainPacket,++ -- * Decode+ decodePacket,+ decodePackets,+ decodeCryptPackets,+ decryptCrypt,+ decodeStatelessResetToken,++ -- * Frame+ encodeFrames,+ decodeFramesBuffer,+ decodeFramesBS,+ countZero, -- testing++ -- * Header+ isLong,+ isShort,+ protectFlags,+ unprotectFlags,+ encodeLongHeaderFlags,+ encodeShortHeaderFlags,+ decodeLongHeaderPacketType,+ encodePktNumLength,+ decodePktNumLength,+ versionNegotiationPacketType,+ retryPacketType,++ -- * Token+ CryptoToken (..),+ isRetryToken,+ generateToken,+ generateRetryToken,+ encryptToken,+ decryptToken,+) where import Network.QUIC.Packet.Decode import Network.QUIC.Packet.Decrypt
Network/QUIC/Packet/Decode.hs view
@@ -1,11 +1,11 @@ {-# LANGUAGE OverloadedStrings #-} module Network.QUIC.Packet.Decode (- decodePacket- , decodePackets- , decodeCryptPackets- , decodeStatelessResetToken- ) where+ decodePacket,+ decodePackets,+ decodeCryptPackets,+ decodeStatelessResetToken,+) where import qualified Data.ByteString as BS import qualified Data.ByteString.Short as Short@@ -18,12 +18,12 @@ ---------------------------------------------------------------- -- Server uses this.-decodeCryptPackets :: ByteString -> IO [(CryptPacket,EncryptionLevel,Int)]+decodeCryptPackets :: ByteString -> IO [(CryptPacket, EncryptionLevel, Int)] decodeCryptPackets bs0 = unwrap <$> decodePackets bs0 where- unwrap (PacketIC c l s:xs) = (c,l,s) : unwrap xs- unwrap (_:xs) = unwrap xs- unwrap [] = []+ unwrap (PacketIC c l s : xs) = (c, l, s) : unwrap xs+ unwrap (_ : xs) = unwrap xs+ unwrap [] = [] -- Client uses this. decodePackets :: ByteString -> IO [PacketI]@@ -52,29 +52,29 @@ decode rbuf proFlags False = do (ver, dCID, sCID) <- decodeLongHeader rbuf case ver of- Negotiation -> do- decodeVersionNegotiationPacket rbuf dCID sCID- _ -> case decodeLongHeaderPacketType ver proFlags of- RetryPacketType -> do- decodeRetryPacket rbuf proFlags ver dCID sCID- RTT0PacketType -> do- let header = RTT0 ver dCID sCID- cpkt <- CryptPacket header <$> makeLongCrypt bs rbuf- siz <- savingSize rbuf- return $ PacketIC cpkt RTT0Level siz- InitialPacketType -> do- tokenLen <- fromIntegral <$> decodeInt' rbuf- token <- extractByteString rbuf tokenLen- let header = Initial ver dCID sCID token- cpkt <- CryptPacket header <$> makeLongCrypt bs rbuf- siz <- savingSize rbuf- return $ PacketIC cpkt InitialLevel siz- HandshakePacketType -> do- let header = Handshake ver dCID sCID- crypt <- CryptPacket header <$> makeLongCrypt bs rbuf- siz <- savingSize rbuf- return $ PacketIC crypt HandshakeLevel siz- handler BufferOverrun = return (PacketIB BrokenPacket,"")+ Negotiation -> do+ decodeVersionNegotiationPacket rbuf dCID sCID+ _ -> case decodeLongHeaderPacketType ver proFlags of+ RetryPacketType -> do+ decodeRetryPacket rbuf proFlags ver dCID sCID+ RTT0PacketType -> do+ let header = RTT0 ver dCID sCID+ cpkt <- CryptPacket header <$> makeLongCrypt bs rbuf+ siz <- savingSize rbuf+ return $ PacketIC cpkt RTT0Level siz+ InitialPacketType -> do+ tokenLen <- fromIntegral <$> decodeInt' rbuf+ token <- extractByteString rbuf tokenLen+ let header = Initial ver dCID sCID token+ cpkt <- CryptPacket header <$> makeLongCrypt bs rbuf+ siz <- savingSize rbuf+ return $ PacketIC cpkt InitialLevel siz+ HandshakePacketType -> do+ let header = Handshake ver dCID sCID+ crypt <- CryptPacket header <$> makeLongCrypt bs rbuf+ siz <- savingSize rbuf+ return $ PacketIC crypt HandshakeLevel siz+ handler BufferOverrun = return (PacketIB BrokenPacket, "") makeShortCrypt :: ByteString -> ReadBuffer -> IO Crypt makeShortCrypt bs rbuf = do@@ -94,12 +94,12 @@ ---------------------------------------------------------------- decodeLongHeader :: ReadBuffer -> IO (Version, CID, CID)-decodeLongHeader rbuf = do- ver <- Version <$> read32 rbuf+decodeLongHeader rbuf = do+ ver <- Version <$> read32 rbuf dcidlen <- fromIntegral <$> read8 rbuf- dCID <- makeCID <$> extractShortByteString rbuf dcidlen+ dCID <- makeCID <$> extractShortByteString rbuf dcidlen scidlen <- fromIntegral <$> read8 rbuf- sCID <- makeCID <$> extractShortByteString rbuf scidlen+ sCID <- makeCID <$> extractShortByteString rbuf scidlen return (ver, dCID, sCID) decodeVersionNegotiationPacket :: ReadBuffer -> CID -> CID -> IO PacketI@@ -109,26 +109,27 @@ return $ PacketIV $ VersionNegotiationPacket dCID sCID vers where decodeVersions siz vers- | siz >= 4 = do+ | siz >= 4 = do ver <- Version <$> read32 rbuf decodeVersions (siz - 4) ((ver :) . vers)- | otherwise = return $ vers []+ | otherwise = return $ vers [] -decodeRetryPacket :: ReadBuffer -> Flags Protected -> Version -> CID -> CID -> IO PacketI+decodeRetryPacket+ :: ReadBuffer -> Flags Protected -> Version -> CID -> CID -> IO PacketI decodeRetryPacket rbuf _proFlags version dCID sCID = do rsiz <- remainingSize rbuf token <- extractByteString rbuf (rsiz - 16) siz <- savingSize rbuf pseudo <- extractByteString rbuf $ negate siz tag <- extractByteString rbuf 16- return $ PacketIR $ RetryPacket version dCID sCID token (Right (pseudo,tag))+ return $ PacketIR $ RetryPacket version dCID sCID token (Right (pseudo, tag)) ---------------------------------------------------------------- decodeStatelessResetToken :: ByteString -> Maybe StatelessResetToken decodeStatelessResetToken bs- | len < 21 = Nothing- | otherwise = Just $ StatelessResetToken $ Short.toShort token+ | len < 21 = Nothing+ | otherwise = Just $ StatelessResetToken $ Short.toShort token where len = BS.length bs- (_,token) = BS.splitAt (len - 16) bs+ (_, token) = BS.splitAt (len - 16) bs
Network/QUIC/Packet/Decrypt.hs view
@@ -1,10 +1,10 @@+{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ExistentialQuantification #-} module Network.QUIC.Packet.Decrypt (- decryptCrypt- ) where+ decryptCrypt,+) where import qualified Data.ByteString as BS import qualified Data.ByteString.Internal as BS@@ -32,40 +32,44 @@ makeMask = unprotect protector Mask mask = makeMask sample case BS.uncons mask of- Nothing -> return Nothing- Just (mask1,mask2) -> do- let rawFlags@(Flags flags) = unprotectFlags proFlags mask1- epnLen = decodePktNumLength rawFlags- epn = BS.take epnLen $ BS.drop cryptPktNumOffset cryptPacket- bytePN = bsXOR mask2 epn- headerLen = cryptPktNumOffset + epnLen- (proHeader, ciphertext) = BS.splitAt headerLen cryptPacket- peerPN <- if lvl == RTT1Level then getPeerPacketNumber conn else return 0- let pn = decodePacketNumber peerPN (toEncodedPacketNumber bytePN) epnLen- header <- BS.create headerLen $ \p -> do- void $ copy p proHeader- poke8 flags p 0- void $ copy (p `plusPtr` cryptPktNumOffset) $ BS.take epnLen bytePN- let keyPhase | lvl == RTT1Level = flags `testBit` 2- | otherwise = False- coder <- getCoder conn lvl keyPhase- siz <- decrypt coder (decryptBuf conn) ciphertext (AssDat header) pn- let rrMask | lvl == RTT1Level = 0x18- | otherwise = 0x0c- marks | flags .&. rrMask == 0 = defaultPlainMarks- | otherwise = setIllegalReservedBits defaultPlainMarks- if siz < 0 then- return Nothing- else do- mframes <- decodeFramesBuffer (decryptBuf conn) siz- case mframes of- Nothing -> do- let marks' = setUnknownFrame marks- return $ Just $ Plain rawFlags pn [] marks'- Just frames -> do- let marks' | null frames = setNoFrames marks- | otherwise = marks- return $ Just $ Plain rawFlags pn frames marks'+ Nothing -> return Nothing+ Just (mask1, mask2) -> do+ let rawFlags@(Flags flags) = unprotectFlags proFlags mask1+ epnLen = decodePktNumLength rawFlags+ epn = BS.take epnLen $ BS.drop cryptPktNumOffset cryptPacket+ bytePN = bsXOR mask2 epn+ headerLen = cryptPktNumOffset + epnLen+ (proHeader, ciphertext) = BS.splitAt headerLen cryptPacket+ peerPN <- if lvl == RTT1Level then getPeerPacketNumber conn else return 0+ let pn = decodePacketNumber peerPN (toEncodedPacketNumber bytePN) epnLen+ header <- BS.create headerLen $ \p -> do+ void $ copy p proHeader+ poke8 flags p 0+ void $ copy (p `plusPtr` cryptPktNumOffset) $ BS.take epnLen bytePN+ let keyPhase+ | lvl == RTT1Level = flags `testBit` 2+ | otherwise = False+ coder <- getCoder conn lvl keyPhase+ siz <- decrypt coder (decryptBuf conn) ciphertext (AssDat header) pn+ let rrMask+ | lvl == RTT1Level = 0x18+ | otherwise = 0x0c+ marks+ | flags .&. rrMask == 0 = defaultPlainMarks+ | otherwise = setIllegalReservedBits defaultPlainMarks+ if siz < 0+ then return Nothing+ else do+ mframes <- decodeFramesBuffer (decryptBuf conn) siz+ case mframes of+ Nothing -> do+ let marks' = setUnknownFrame marks+ return $ Just $ Plain rawFlags pn [] marks'+ Just frames -> do+ let marks'+ | null frames = setNoFrames marks+ | otherwise = marks+ return $ Just $ Plain rawFlags pn frames marks' toEncodedPacketNumber :: ByteString -> EncodedPacketNumber toEncodedPacketNumber bs = foldl' (\b a -> b * 256 + fromIntegral a) 0 $ BS.unpack bs
Network/QUIC/Packet/Encode.hs view
@@ -1,11 +1,11 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Packet.Encode (--- encodePacket- encodeVersionNegotiationPacket- , encodeRetryPacket- , encodePlainPacket- ) where+ -- encodePacket+ encodeVersionNegotiationPacket,+ encodeRetryPacket,+ encodePlainPacket,+) where import qualified Data.ByteString as BS import Foreign.ForeignPtr@@ -24,6 +24,7 @@ ---------------------------------------------------------------- -- | This is not used internally.+ {- encodePacket :: Connection -> PacketO -> IO [ByteString] encodePacket _ (PacketOV pkt) = (:[]) <$> encodeVersionNegotiationPacket pkt@@ -42,6 +43,7 @@ -- vers mapM_ (\(Version ver) -> write32 wbuf ver) vers -- no header protection+ return () ---------------------------------------------------------------- @@ -57,8 +59,9 @@ let tag = calculateIntegrityTag ver odCID pseudo0 copyByteString wbuf tag -- no header protection+ return () -- only for testing-encodeRetryPacket (RetryPacket ver dCID sCID token (Right (_,tag))) = withWriteBuffer maximumQUICHeaderSize $ \wbuf -> do+encodeRetryPacket (RetryPacket ver dCID sCID token (Right (_, tag))) = withWriteBuffer maximumQUICHeaderSize $ \wbuf -> do save wbuf Flags flags <- retryPacketType ver write8 wbuf flags@@ -66,64 +69,112 @@ copyByteString wbuf token copyByteString wbuf tag -- no header protection+ return () ---------------------------------------------------------------- -- WriteBuffer: protect(header) + encrypt(plain_frames) -- encodeBuf: plain_frames -encodePlainPacket :: Connection -> SizedBuffer -> PlainPacket -> Maybe Int -> IO (Int,Int)+encodePlainPacket+ :: Connection -> SizedBuffer -> PlainPacket -> Maybe Int -> IO (Int, Int) encodePlainPacket conn (SizedBuffer buf bufsiz) ppkt@(PlainPacket _ plain) mlen = do- let mlen' | isNoPaddings (plainMarks plain) = Nothing- | otherwise = mlen+ let mlen'+ | isNoPaddings (plainMarks plain) = Nothing+ | otherwise = mlen wbuf <- newWriteBuffer buf bufsiz encodePlainPacket' conn wbuf ppkt mlen' -encodePlainPacket' :: Connection -> WriteBuffer -> PlainPacket -> Maybe Int -> IO (Int,Int)+encodePlainPacket'+ :: Connection -> WriteBuffer -> PlainPacket -> Maybe Int -> IO (Int, Int) encodePlainPacket' conn wbuf (PlainPacket (Initial ver dCID sCID token) (Plain flags pn frames _)) mlen = do headerBeg <- currentOffset wbuf -- flag ... sCID- (epn, epnLen) <- encodeLongHeaderPP conn wbuf InitialPacketType ver dCID sCID flags pn+ (epn, epnLen) <-+ encodeLongHeaderPP conn wbuf InitialPacketType ver dCID sCID flags pn -- token encodeInt' wbuf $ fromIntegral $ BS.length token copyByteString wbuf token -- length .. payload- protectPayloadHeader conn wbuf frames pn epn epnLen headerBeg mlen InitialLevel False-+ protectPayloadHeader+ conn+ wbuf+ frames+ pn+ epn+ epnLen+ headerBeg+ mlen+ InitialLevel+ False encodePlainPacket' conn wbuf (PlainPacket (RTT0 ver dCID sCID) (Plain flags pn frames _)) mlen = do headerBeg <- currentOffset wbuf -- flag ... sCID- (epn, epnLen) <- encodeLongHeaderPP conn wbuf RTT0PacketType ver dCID sCID flags pn+ (epn, epnLen) <-+ encodeLongHeaderPP conn wbuf RTT0PacketType ver dCID sCID flags pn -- length .. payload- protectPayloadHeader conn wbuf frames pn epn epnLen headerBeg mlen RTT0Level False-+ protectPayloadHeader+ conn+ wbuf+ frames+ pn+ epn+ epnLen+ headerBeg+ mlen+ RTT0Level+ False encodePlainPacket' conn wbuf (PlainPacket (Handshake ver dCID sCID) (Plain flags pn frames _)) mlen = do headerBeg <- currentOffset wbuf -- flag ... sCID- (epn, epnLen) <- encodeLongHeaderPP conn wbuf HandshakePacketType ver dCID sCID flags pn+ (epn, epnLen) <-+ encodeLongHeaderPP conn wbuf HandshakePacketType ver dCID sCID flags pn -- length .. payload- protectPayloadHeader conn wbuf frames pn epn epnLen headerBeg mlen HandshakeLevel False-+ protectPayloadHeader+ conn+ wbuf+ frames+ pn+ epn+ epnLen+ headerBeg+ mlen+ HandshakeLevel+ False encodePlainPacket' conn wbuf (PlainPacket (Short dCID) (Plain flags pn frames marks)) mlen = do headerBeg <- currentOffset wbuf -- flag- let (epn, epnLen) | is4bytesPN marks = (fromIntegral pn, 4)- | otherwise = encodePacketNumber 0 {- dummy -} pn+ let (epn, epnLen)+ | is4bytesPN marks = (fromIntegral pn, 4)+ | otherwise = encodePacketNumber 0 {- dummy -} pn pp = encodePktNumLength epnLen quicBit <- greaseQuicBit <$> getPeerParameters conn- (keyPhase,_) <- getCurrentKeyPhase conn+ (keyPhase, _) <- getCurrentKeyPhase conn Flags flags' <- encodeShortHeaderFlags flags pp quicBit keyPhase write8 wbuf flags' -- dCID let (dcid, _) = unpackCID dCID copyShortByteString wbuf dcid- protectPayloadHeader conn wbuf frames pn epn epnLen headerBeg mlen RTT1Level keyPhase+ protectPayloadHeader+ conn+ wbuf+ frames+ pn+ epn+ epnLen+ headerBeg+ mlen+ RTT1Level+ keyPhase ---------------------------------------------------------------- -encodeLongHeader :: WriteBuffer- -> Version -> CID -> CID- -> IO ()+encodeLongHeader+ :: WriteBuffer+ -> Version+ -> CID+ -> CID+ -> IO () encodeLongHeader wbuf (Version ver) dCID sCID = do write32 wbuf ver let (dcid, dcidlen) = unpackCID dCID@@ -135,11 +186,16 @@ ---------------------------------------------------------------- -encodeLongHeaderPP :: Connection -> WriteBuffer- -> LongHeaderPacketType -> Version -> CID -> CID- -> Flags Raw- -> PacketNumber- -> IO (EncodedPacketNumber, Int)+encodeLongHeaderPP+ :: Connection+ -> WriteBuffer+ -> LongHeaderPacketType+ -> Version+ -> CID+ -> CID+ -> Flags Raw+ -> PacketNumber+ -> IO (EncodedPacketNumber, Int) encodeLongHeaderPP conn wbuf pkttyp ver dCID sCID flags pn = do let el@(_, pnLen) = encodePacketNumber 0 {- dummy -} pn pp = encodePktNumLength pnLen@@ -151,17 +207,28 @@ ---------------------------------------------------------------- -protectPayloadHeader :: Connection -> WriteBuffer -> [Frame] -> PacketNumber -> EncodedPacketNumber -> Int -> Buffer -> Maybe Int -> EncryptionLevel -> Bool -> IO (Int,Int)+protectPayloadHeader+ :: Connection+ -> WriteBuffer+ -> [Frame]+ -> PacketNumber+ -> EncodedPacketNumber+ -> Int+ -> Buffer+ -> Maybe Int+ -> EncryptionLevel+ -> Bool+ -> IO (Int, Int) protectPayloadHeader conn wbuf frames pn epn epnLen headerBeg mlen lvl keyPhase = do -- Real size is maximumUdpPayloadSize. But smaller is better.- let encBuf = encodeBuf conn+ let encBuf = encodeBuf conn encBufLen = 1500 - 20 - 8 payloadWithoutPaddingSiz <- encodeFramesWithPadding encBuf encBufLen frames cipher <- getCipher conn lvl -- before length or packer number lengthOrPNBeg <- currentOffset wbuf- (packetLen, headerLen, plainLen, tagLen, padLen)- <- calcLen cipher lengthOrPNBeg payloadWithoutPaddingSiz+ (packetLen, headerLen, plainLen, tagLen, padLen) <-+ calcLen cipher lengthOrPNBeg payloadWithoutPaddingSiz when (lvl /= RTT1Level) $ writeLen (epnLen + plainLen + tagLen) pnBeg <- currentOffset wbuf writeEpn epnLen@@ -177,28 +244,29 @@ len <- encrypt coder cryptoBeg plaintext (AssDat header) pn maskBeg <- getMask protector --- if len < 0 || maskBeg == nullPtr then- return (-1, -1)- else do- -- protecting header- protectHeader headerBeg pnBeg epnLen maskBeg- return (packetLen, padLen)+ if len < 0 || maskBeg == nullPtr+ then return (-1, -1)+ else do+ -- protecting header+ protectHeader headerBeg pnBeg epnLen maskBeg+ return (packetLen, padLen) where calcLen cipher lengthOrPNBeg payloadWithoutPaddingSiz = do- let headerLen = (lengthOrPNBeg `minusPtr` headerBeg)- -- length: assuming 2byte length- + (if lvl /= RTT1Level then 2 else 0)- + epnLen+ let headerLen =+ (lengthOrPNBeg `minusPtr` headerBeg)+ -- length: assuming 2byte length+ + (if lvl /= RTT1Level then 2 else 0)+ + epnLen let tagLen = tagLength cipher plainLen = case mlen of- Nothing -> payloadWithoutPaddingSiz+ Nothing -> payloadWithoutPaddingSiz Just expectedLen -> expectedLen - headerLen - tagLen packetLen = headerLen + plainLen + tagLen padLen = plainLen - payloadWithoutPaddingSiz return (packetLen, headerLen, plainLen, tagLen, padLen) -- length: assuming 2byte length writeLen len = encodeInt'2 wbuf $ fromIntegral len- writeEpn 1 = write8 wbuf $ fromIntegral epn+ writeEpn 1 = write8 wbuf $ fromIntegral epn writeEpn 2 = write16 wbuf $ fromIntegral epn writeEpn 3 = write24 wbuf epn writeEpn _ = write32 wbuf epn
Network/QUIC/Packet/Frame.hs view
@@ -1,17 +1,17 @@ {-# LANGUAGE OverloadedStrings #-} module Network.QUIC.Packet.Frame (- encodeFrames- , encodeFramesWithPadding- , decodeFramesBS- , decodeFramesBuffer- , countZero -- testing- ) where+ encodeFrames,+ encodeFramesWithPadding,+ decodeFramesBS,+ decodeFramesBuffer,+ countZero, -- testing+) where import qualified Data.ByteString as BS import qualified Data.ByteString.Short as Short-import Foreign.Ptr (Ptr, plusPtr, minusPtr, alignPtr, castPtr)-import Foreign.Storable (peek, alignment)+import Foreign.Ptr (Ptr, alignPtr, castPtr, minusPtr, plusPtr)+import Foreign.Storable (alignment, peek) import Network.Socket.Internal (zeroMemory) import Network.QUIC.Imports@@ -21,12 +21,14 @@ encodeFrames :: [Frame] -> IO ByteString encodeFrames frames = withWriteBuffer 2048 $ \wbuf ->- mapM_ (encodeFrame wbuf) frames+ mapM_ (encodeFrame wbuf) frames -encodeFramesWithPadding :: Buffer- -> BufferSize- -> [Frame]- -> IO Int -- ^ payload size without paddings+encodeFramesWithPadding+ :: Buffer+ -> BufferSize+ -> [Frame]+ -> IO Int+ -- ^ payload size without paddings encodeFramesWithPadding buf siz frames = do zeroMemory buf $ fromIntegral siz -- padding wbuf <- newWriteBuffer buf siz@@ -45,7 +47,7 @@ encodeInt' wbuf $ fromIntegral range1 mapM_ putRanges ranges where- putRanges (gap,rng) = do+ putRanges (gap, rng) = do encodeInt' wbuf $ fromIntegral gap encodeInt' wbuf $ fromIntegral rng encodeFrame wbuf (ResetStream sid (ApplicationProtocolError err) finalLen) = do@@ -68,10 +70,12 @@ copyByteString wbuf token encodeFrame wbuf (StreamF sid off dats fin) = do let flag0 = 0x08 .|. 0x02 -- len- flag1 | off /= 0 = flag0 .|. 0x04 -- off- | otherwise = flag0- flag2 | fin = flag1 .|. 0x01 -- fin- | otherwise = flag1+ flag1+ | off /= 0 = flag0 .|. 0x04 -- off+ | otherwise = flag0+ flag2+ | fin = flag1 .|. 0x01 -- fin+ | otherwise = flag1 write8 wbuf flag2 encodeInt' wbuf $ fromIntegral sid when (off /= 0) $ encodeInt' wbuf $ fromIntegral off@@ -86,8 +90,8 @@ encodeInt' wbuf $ fromIntegral n encodeFrame wbuf (MaxStreams dir ms) = do case dir of- Bidirectional -> write8 wbuf 0x12- Unidirectional -> write8 wbuf 0x13+ Bidirectional -> write8 wbuf 0x12+ Unidirectional -> write8 wbuf 0x13 encodeInt' wbuf $ fromIntegral ms encodeFrame wbuf (DataBlocked n) = do write8 wbuf 0x14@@ -98,8 +102,8 @@ encodeInt' wbuf $ fromIntegral n encodeFrame wbuf (StreamsBlocked dir ms) = do case dir of- Bidirectional -> write8 wbuf 0x16- Unidirectional -> write8 wbuf 0x17+ Bidirectional -> write8 wbuf 0x16+ Unidirectional -> write8 wbuf 0x17 encodeInt' wbuf $ fromIntegral ms encodeFrame wbuf (NewConnectionID cidInfo rpt) = do write8 wbuf 0x18@@ -148,47 +152,47 @@ where loop frames = do ok <- (>= 1) <$> remainingSize rbuf- if ok then do- frame <- decodeFrame rbuf- case frame of- UnknownFrame _ -> return Nothing- _ -> loop (frames . (frame:))- else- return $ Just $ frames []+ if ok+ then do+ frame <- decodeFrame rbuf+ case frame of+ UnknownFrame _ -> return Nothing+ _ -> loop (frames . (frame :))+ else return $ Just $ frames [] decodeFrame :: ReadBuffer -> IO Frame decodeFrame rbuf = do ftyp <- fromIntegral <$> decodeInt' rbuf case ftyp :: FrameType of- 0x00 -> decodePadding rbuf- 0x01 -> return Ping- 0x02 -> decodeAck rbuf- -- 0x03 -> Ack with ECN Counts- 0x04 -> decodeResetStream rbuf- 0x05 -> decodeStopSending rbuf- 0x06 -> decodeCrypto rbuf- 0x07 -> decodeNewToken rbuf- x | 0x08 <= x && x <= 0x0f -> do- let off = testBit x 2- len = testBit x 1- fin = testBit x 0- decodeStream rbuf off len fin- 0x10 -> decodeMaxData rbuf- 0x11 -> decodeMaxStreamData rbuf- 0x12 -> decodeMaxStreams rbuf Bidirectional- 0x13 -> decodeMaxStreams rbuf Unidirectional- 0x14 -> decodeDataBlocked rbuf- 0x15 -> decodeStreamDataBlocked rbuf- 0x16 -> decodeStreamsBlocked rbuf Bidirectional- 0x17 -> decodeStreamsBlocked rbuf Unidirectional- 0x18 -> decodeNewConnectionID rbuf- 0x19 -> decodeRetireConnectionID rbuf- 0x1a -> decodePathChallenge rbuf- 0x1b -> decodePathResponse rbuf- 0x1c -> decodeConnectionClose rbuf- 0x1d -> decodeConnectionCloseApp rbuf- 0x1e -> return HandshakeDone- x -> return $ UnknownFrame x+ 0x00 -> decodePadding rbuf+ 0x01 -> return Ping+ 0x02 -> decodeAck rbuf+ -- 0x03 -> Ack with ECN Counts+ 0x04 -> decodeResetStream rbuf+ 0x05 -> decodeStopSending rbuf+ 0x06 -> decodeCrypto rbuf+ 0x07 -> decodeNewToken rbuf+ x | 0x08 <= x && x <= 0x0f -> do+ let off = testBit x 2+ len = testBit x 1+ fin = testBit x 0+ decodeStream rbuf off len fin+ 0x10 -> decodeMaxData rbuf+ 0x11 -> decodeMaxStreamData rbuf+ 0x12 -> decodeMaxStreams rbuf Bidirectional+ 0x13 -> decodeMaxStreams rbuf Unidirectional+ 0x14 -> decodeDataBlocked rbuf+ 0x15 -> decodeStreamDataBlocked rbuf+ 0x16 -> decodeStreamsBlocked rbuf Bidirectional+ 0x17 -> decodeStreamsBlocked rbuf Unidirectional+ 0x18 -> decodeNewConnectionID rbuf+ 0x19 -> decodeRetireConnectionID rbuf+ 0x1a -> decodePathChallenge rbuf+ 0x1b -> decodePathResponse rbuf+ 0x1c -> decodeConnectionClose rbuf+ 0x1d -> decodeConnectionCloseApp rbuf+ 0x1e -> return HandshakeDone+ x -> return $ UnknownFrame x decodePadding :: ReadBuffer -> IO Frame decodePadding rbuf = do@@ -201,39 +205,38 @@ countZero :: Ptr Word8 -> Ptr Word8 -> IO Int countZero beg0 end0- | (end0 `minusPtr` beg0) <= ali = fst <$> countBy1 beg0 end0 0- | otherwise = do- let beg1 = alignPtr beg0 ali- end1' = alignPtr end0 ali- end1 | end0 == end1' = end1'- | otherwise = end1' `plusPtr` negate ali- (n1,cont1) <- countBy1 beg0 beg1 0- if not cont1 then- return n1- else do- (n2,beg2) <- countBy8 (castPtr beg1) (castPtr end1) 0- (n3,_) <- countBy1 (castPtr beg2) end0 0- return (n1 + n2 + n3)+ | (end0 `minusPtr` beg0) <= ali = fst <$> countBy1 beg0 end0 0+ | otherwise = do+ let beg1 = alignPtr beg0 ali+ end1' = alignPtr end0 ali+ end1+ | end0 == end1' = end1'+ | otherwise = end1' `plusPtr` negate ali+ (n1, cont1) <- countBy1 beg0 beg1 0+ if not cont1+ then return n1+ else do+ (n2, beg2) <- countBy8 (castPtr beg1) (castPtr end1) 0+ (n3, _) <- countBy1 (castPtr beg2) end0 0+ return (n1 + n2 + n3) where ali = alignment (0 :: Word64)- countBy1 :: Ptr Word8 -> Ptr Word8 -> Int -> IO (Int,Bool)+ countBy1 :: Ptr Word8 -> Ptr Word8 -> Int -> IO (Int, Bool) countBy1 beg end n- | beg < end = do+ | beg < end = do ftyp <- peek beg- if ftyp == 0 then- countBy1 (beg `plusPtr` 1) end (n + 1)- else- return (n, False)- | otherwise = return (n, True)+ if ftyp == 0+ then countBy1 (beg `plusPtr` 1) end (n + 1)+ else return (n, False)+ | otherwise = return (n, True) countBy8 :: Ptr Word64 -> Ptr Word64 -> Int -> IO (Int, Ptr Word64) countBy8 beg end n- | beg < end = do+ | beg < end = do ftyp <- peek beg- if ftyp == 0 then- countBy8 (beg `plusPtr` ali) end (n + ali)- else- return (n, beg)- | otherwise = return (n, beg)+ if ftyp == 0+ then countBy8 (beg `plusPtr` ali) end (n + ali)+ else return (n, beg)+ | otherwise = return (n, beg) decodeCrypto :: ReadBuffer -> IO Frame decodeCrypto rbuf = do@@ -245,10 +248,10 @@ decodeAck :: ReadBuffer -> IO Frame decodeAck rbuf = do largest <- fromIntegral <$> decodeInt' rbuf- delay <- fromIntegral <$> decodeInt' rbuf- count <- fromIntegral <$> decodeInt' rbuf- range1 <- fromIntegral <$> decodeInt' rbuf- ranges <- getRanges count id+ delay <- fromIntegral <$> decodeInt' rbuf+ count <- fromIntegral <$> decodeInt' rbuf+ range1 <- fromIntegral <$> decodeInt' rbuf+ ranges <- getRanges count id return $ Ack (AckInfo largest range1 ranges) $ Milliseconds delay where getRanges 0 build = return $ build []@@ -279,16 +282,18 @@ decodeStream :: ReadBuffer -> Bool -> Bool -> Bool -> IO Frame decodeStream rbuf hasOff hasLen fin = do sID <- fromIntegral <$> decodeInt' rbuf- off <- if hasOff then- fromIntegral <$> decodeInt' rbuf- else- return 0- dat <- if hasLen then do- len <- fromIntegral <$> decodeInt' rbuf- extractByteString rbuf len- else do- len <- remainingSize rbuf- extractByteString rbuf len+ off <-+ if hasOff+ then fromIntegral <$> decodeInt' rbuf+ else return 0+ dat <-+ if hasLen+ then do+ len <- fromIntegral <$> decodeInt' rbuf+ extractByteString rbuf len+ else do+ len <- remainingSize rbuf+ extractByteString rbuf len return $ StreamF sID off [dat] fin ----------------------------------------------------------------@@ -323,16 +328,16 @@ decodeConnectionClose :: ReadBuffer -> IO Frame decodeConnectionClose rbuf = do- err <- TransportError . fromIntegral <$> decodeInt' rbuf- ftyp <- fromIntegral <$> decodeInt' rbuf- len <- fromIntegral <$> decodeInt' rbuf+ err <- TransportError . fromIntegral <$> decodeInt' rbuf+ ftyp <- fromIntegral <$> decodeInt' rbuf+ len <- fromIntegral <$> decodeInt' rbuf reason <- extractShortByteString rbuf len return $ ConnectionClose err ftyp reason -decodeConnectionCloseApp :: ReadBuffer -> IO Frame+decodeConnectionCloseApp :: ReadBuffer -> IO Frame decodeConnectionCloseApp rbuf = do- err <- ApplicationProtocolError . fromIntegral <$> decodeInt' rbuf- len <- fromIntegral <$> decodeInt' rbuf+ err <- ApplicationProtocolError . fromIntegral <$> decodeInt' rbuf+ len <- fromIntegral <$> decodeInt' rbuf reason <- extractShortByteString rbuf len return $ ConnectionCloseApp err reason
Network/QUIC/Packet/Header.hs view
@@ -1,18 +1,18 @@ {-# LANGUAGE BinaryLiterals #-} module Network.QUIC.Packet.Header (- isLong- , isShort- , protectFlags- , unprotectFlags- , encodeLongHeaderFlags- , encodeShortHeaderFlags- , decodeLongHeaderPacketType- , encodePktNumLength- , decodePktNumLength- , versionNegotiationPacketType- , retryPacketType- ) where+ isLong,+ isShort,+ protectFlags,+ unprotectFlags,+ encodeLongHeaderFlags,+ encodeShortHeaderFlags,+ decodeLongHeaderPacketType,+ encodePktNumLength,+ decodePktNumLength,+ versionNegotiationPacketType,+ retryPacketType,+) where import Network.QUIC.Imports import Network.QUIC.Types@@ -39,43 +39,56 @@ mask = mask1 .&. flagBits flags proFlags = flags `xor` mask - {-# INLINE flagBits #-}+{- FOURMOLU_DISABLE -} flagBits :: Word8 -> Word8 flagBits flags- | isLong flags = 0b00001111 -- long header- | otherwise = 0b00011111 -- short header+ | isLong flags = 0b00001111 -- long header+ | otherwise = 0b00011111 -- short header+{- FOURMOLU_ENABLE -} ---------------------------------------------------------------- randomizeQuicBit :: Word8 -> Bool -> IO Word8 randomizeQuicBit flags quicBit- | quicBit = do+ | quicBit = do r <- getRandomOneByte return ((flags .&. 0b10111111) .|. (r .&. 0b01000000))- | otherwise = return flags+ | otherwise = return flags {-# INLINE encodeShortHeaderFlags #-}-encodeShortHeaderFlags :: Flags Raw -> Flags Raw -> Bool -> Bool -> IO (Flags Raw)+{- FOURMOLU_DISABLE -}+encodeShortHeaderFlags+ :: Flags Raw -> Flags Raw -> Bool -> Bool -> IO (Flags Raw) encodeShortHeaderFlags (Flags fg) (Flags pp) quicBit keyPhase = Flags <$> randomizeQuicBit flags quicBit where- flags = 0b01000000- .|. (fg .&. 0b00111100)- .|. (pp .&. 0b00000011)- .|. (if keyPhase then 0b00000100 else 0b00000000)+ flags =+ 0b01000000+ .|. (fg .&. 0b00111100)+ .|. (pp .&. 0b00000011)+ .|. (if keyPhase then 0b00000100 else 0b00000000)+{- FOURMOLU_ENABLE -} {-# INLINE encodeLongHeaderFlags #-}-encodeLongHeaderFlags :: Version -> LongHeaderPacketType -> Flags Raw -> Flags Raw -> Bool -> IO (Flags Raw)+encodeLongHeaderFlags+ :: Version+ -> LongHeaderPacketType+ -> Flags Raw+ -> Flags Raw+ -> Bool+ -> IO (Flags Raw) encodeLongHeaderFlags ver typ (Flags fg) (Flags pp) quicBit = Flags <$> randomizeQuicBit flags quicBit where Flags tp = longHeaderPacketType ver typ- flags = tp- .|. (fg .&. 0b00001100)- .|. (pp .&. 0b00000011)+ flags =+ tp+ .|. (fg .&. 0b00001100)+ .|. (pp .&. 0b00000011) {-# INLINE longHeaderPacketType #-}+{- FOURMOLU_DISABLE -} longHeaderPacketType :: Version -> LongHeaderPacketType -> Flags Raw longHeaderPacketType Version2 InitialPacketType = Flags 0b11010000 longHeaderPacketType Version2 RTT0PacketType = Flags 0b11100000@@ -85,6 +98,7 @@ longHeaderPacketType _ RTT0PacketType = Flags 0b11010000 longHeaderPacketType _ HandshakePacketType = Flags 0b11100000 longHeaderPacketType _ RetryPacketType = Flags 0b11110000+{- FOURMOLU_ENABLE -} retryPacketType :: Version -> IO (Flags Raw) retryPacketType Version2 = do@@ -103,6 +117,7 @@ return $ Flags flags {-# INLINE decodeLongHeaderPacketType #-}+{- FOURMOLU_DISABLE -} decodeLongHeaderPacketType :: Version -> Flags Protected -> LongHeaderPacketType decodeLongHeaderPacketType Version2 (Flags flags) = case flags .&. 0b00110000 of 0b00010000 -> InitialPacketType@@ -114,6 +129,7 @@ 0b00010000 -> RTT0PacketType 0b00100000 -> HandshakePacketType _ -> RetryPacketType+{- FOURMOLU_ENABLE -} {-# INLINE encodePktNumLength #-} encodePktNumLength :: Int -> Flags Raw
Network/QUIC/Packet/Number.hs view
@@ -1,7 +1,7 @@ module Network.QUIC.Packet.Number (- encodePacketNumber- , decodePacketNumber- ) where+ encodePacketNumber,+ decodePacketNumber,+) where import Network.QUIC.Imports import Network.QUIC.Types@@ -16,6 +16,7 @@ -- True -- >>> encodePacketNumber 0xabe8bc 0xace8fe == (0xace8fe, 3) -- True+{- FOURMOLU_DISABLE -} encodePacketNumber :: PacketNumber -> PacketNumber -> (EncodedPacketNumber, Int) encodePacketNumber largestPN pn = (diff, bytes) where@@ -26,6 +27,7 @@ | enoughRange < 16777216 = (0x00ffffff, 3) | otherwise = (0xffffffff, 4) diff = fromIntegral (pn .&. pnMask)+{- FOURMOLU_ENABLE -} ---------------------------------------------------------------- @@ -37,6 +39,7 @@ -- True -- >>> decodePacketNumber 0xabe8bc 0xace8fe 3 == 0xace8fe -- True+{- FOURMOLU_DISABLE -} decodePacketNumber :: PacketNumber -> EncodedPacketNumber -> Int -> PacketNumber decodePacketNumber largestPN truncatedPN bytes | candidatePN <= expectedPN - pnHwin@@ -53,3 +56,4 @@ pnMask = pnWin - 1 candidatePN = (expectedPN .&. complement pnMask) .|. fromIntegral truncatedPN+{- FOURMOLU_ENABLE -}
Network/QUIC/Packet/Token.hs view
@@ -1,11 +1,11 @@ module Network.QUIC.Packet.Token (- CryptoToken(..)- , isRetryToken- , generateToken- , generateRetryToken- , encryptToken- , decryptToken- ) where+ CryptoToken (..),+ isRetryToken,+ generateToken,+ generateRetryToken,+ encryptToken,+ decryptToken,+) where import qualified Crypto.Token as CT import Data.UnixTime@@ -19,11 +19,11 @@ ---------------------------------------------------------------- -data CryptoToken = CryptoToken {- tokenQUICVersion :: Version- , tokenCreatedTime :: TimeMicrosecond- , tokenCIDs :: Maybe (CID, CID, CID) -- local, remote, orig local- }+data CryptoToken = CryptoToken+ { tokenQUICVersion :: Version+ , tokenCreatedTime :: TimeMicrosecond+ , tokenCIDs :: Maybe (CID, CID, CID) -- local, remote, orig local+ } isRetryToken :: CryptoToken -> Bool isRetryToken token = isJust $ tokenCIDs token@@ -38,7 +38,7 @@ generateRetryToken :: Version -> CID -> CID -> CID -> IO CryptoToken generateRetryToken ver l r o = do t <- getTimeMicrosecond- return $ CryptoToken ver t $ Just (l,r,o)+ return $ CryptoToken ver t $ Just (l, r, o) ---------------------------------------------------------------- @@ -55,21 +55,21 @@ -- length includes its field instance Storable CryptoToken where- sizeOf ~_ = cryptoTokenSize+ sizeOf ~_ = cryptoTokenSize alignment ~_ = 4 peek ptr = do rbuf <- newReadBuffer (castPtr ptr) cryptoTokenSize- ver <- Version <$> read32 rbuf+ ver <- Version <$> read32 rbuf s <- CTime . fromIntegral <$> read64 rbuf let tim = UnixTime s 0 typ <- read8 rbuf case typ of- 0 -> return $ CryptoToken ver tim Nothing- _ -> do- l <- pick rbuf- r <- pick rbuf- o <- pick rbuf- return $ CryptoToken ver tim $ Just (l,r,o)+ 0 -> return $ CryptoToken ver tim Nothing+ _ -> do+ l <- pick rbuf+ r <- pick rbuf+ o <- pick rbuf+ return $ CryptoToken ver tim $ Just (l, r, o) where pick rbuf = do xlen0 <- fromIntegral <$> read8 rbuf@@ -83,12 +83,12 @@ let CTime s = utSeconds tim write64 wbuf $ fromIntegral s case mcids of- Nothing -> write8 wbuf 0- Just (l,r,o) -> do- write8 wbuf 1- bury wbuf l- bury wbuf r- bury wbuf o+ Nothing -> write8 wbuf 0+ Just (l, r, o) -> do+ write8 wbuf 1+ bury wbuf l+ bury wbuf r+ bury wbuf o where bury wbuf x = do let (xcid, xlen) = unpackCID x
Network/QUIC/Parameters.hs view
@@ -1,18 +1,18 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RecordWildCards #-} module Network.QUIC.Parameters (- Parameters(..)- , defaultParameters- , baseParameters -- only for Connection- , encodeParameters- , decodeParameters- , AuthCIDs(..)- , defaultAuthCIDs- , setCIDsToParameters- , getCIDsToParameters- ) where+ Parameters (..),+ defaultParameters,+ baseParameters, -- only for Connection+ encodeParameters,+ decodeParameters,+ AuthCIDs (..),+ defaultAuthCIDs,+ setCIDsToParameters,+ getCIDsToParameters,+) where import qualified Data.ByteString as BS import qualified Data.ByteString.Short as Short@@ -30,8 +30,9 @@ newtype Key = Key Word32 deriving (Eq, Show) type Value = ByteString -type ParameterList = [(Key,Value)]+type ParameterList = [(Key, Value)] +{- FOURMOLU_DISABLE -} pattern OriginalDestinationConnectionId :: Key pattern OriginalDestinationConnectionId = Key 0x00 pattern MaxIdleTimeout :: Key@@ -72,56 +73,58 @@ pattern Grease = Key 0xff pattern GreaseQuicBit :: Key pattern GreaseQuicBit = Key 0x2ab2-+{- FOURMOLU_ENABLE -} -- | QUIC transport parameters.-data Parameters = Parameters {- originalDestinationConnectionId :: Maybe CID- , maxIdleTimeout :: Milliseconds- , statelessResetToken :: Maybe StatelessResetToken -- 16 bytes- , maxUdpPayloadSize :: Int- , initialMaxData :: Int- , initialMaxStreamDataBidiLocal :: Int- , initialMaxStreamDataBidiRemote :: Int- , initialMaxStreamDataUni :: Int- , initialMaxStreamsBidi :: Int- , initialMaxStreamsUni :: Int- , ackDelayExponent :: Int- , maxAckDelay :: Milliseconds- , disableActiveMigration :: Bool- , preferredAddress :: Maybe ByteString -- fixme- , activeConnectionIdLimit :: Int- , initialSourceConnectionId :: Maybe CID- , retrySourceConnectionId :: Maybe CID- , grease :: Maybe ByteString- , greaseQuicBit :: Bool- , versionInformation :: Maybe VersionInfo- } deriving (Eq,Show)+data Parameters = Parameters+ { originalDestinationConnectionId :: Maybe CID+ , maxIdleTimeout :: Milliseconds+ , statelessResetToken :: Maybe StatelessResetToken -- 16 bytes+ , maxUdpPayloadSize :: Int+ , initialMaxData :: Int+ , initialMaxStreamDataBidiLocal :: Int+ , initialMaxStreamDataBidiRemote :: Int+ , initialMaxStreamDataUni :: Int+ , initialMaxStreamsBidi :: Int+ , initialMaxStreamsUni :: Int+ , ackDelayExponent :: Int+ , maxAckDelay :: Milliseconds+ , disableActiveMigration :: Bool+ , preferredAddress :: Maybe ByteString -- fixme+ , activeConnectionIdLimit :: Int+ , initialSourceConnectionId :: Maybe CID+ , retrySourceConnectionId :: Maybe CID+ , grease :: Maybe ByteString+ , greaseQuicBit :: Bool+ , versionInformation :: Maybe VersionInfo+ }+ deriving (Eq, Show) -- | The default value for QUIC transport parameters. baseParameters :: Parameters-baseParameters = Parameters {- originalDestinationConnectionId = Nothing- , maxIdleTimeout = Milliseconds 0 -- disabled- , statelessResetToken = Nothing- , maxUdpPayloadSize = 65527- , initialMaxData = 0- , initialMaxStreamDataBidiLocal = 0- , initialMaxStreamDataBidiRemote = 0- , initialMaxStreamDataUni = 0- , initialMaxStreamsBidi = 0- , initialMaxStreamsUni = 0- , ackDelayExponent = 3- , maxAckDelay = Milliseconds 25- , disableActiveMigration = False- , preferredAddress = Nothing- , activeConnectionIdLimit = 2- , initialSourceConnectionId = Nothing- , retrySourceConnectionId = Nothing- , grease = Nothing- , greaseQuicBit = False- , versionInformation = Nothing- }+baseParameters =+ Parameters+ { originalDestinationConnectionId = Nothing+ , maxIdleTimeout = Milliseconds 0 -- disabled+ , statelessResetToken = Nothing+ , maxUdpPayloadSize = 65527+ , initialMaxData = 0+ , initialMaxStreamDataBidiLocal = 0+ , initialMaxStreamDataBidiRemote = 0+ , initialMaxStreamDataUni = 0+ , initialMaxStreamsBidi = 0+ , initialMaxStreamsUni = 0+ , ackDelayExponent = 3+ , maxAckDelay = Milliseconds 25+ , disableActiveMigration = False+ , preferredAddress = Nothing+ , activeConnectionIdLimit = 2+ , initialSourceConnectionId = Nothing+ , retrySourceConnectionId = Nothing+ , grease = Nothing+ , greaseQuicBit = False+ , versionInformation = Nothing+ } decInt :: ByteString -> Int decInt = fromIntegral . decodeInt@@ -136,7 +139,7 @@ encMilliseconds (Milliseconds n) = encodeInt $ fromIntegral n fromVersionInfo :: Maybe VersionInfo -> Value-fromVersionInfo Nothing = "" -- never reach+fromVersionInfo Nothing = "" -- never reach fromVersionInfo (Just VersionInfo{..}) = unsafeDupablePerformIO $ withWriteBuffer len $ \wbuf -> do let putVersion (Version ver) = write32 wbuf ver@@ -147,95 +150,112 @@ toVersionInfo :: Value -> Maybe VersionInfo toVersionInfo bs- | len < 3 || remainder /= 0 = Just brokenVersionInfo- | otherwise = Just $ unsafeDupablePerformIO $- withReadBuffer bs $ \rbuf -> do- let getVersion = Version <$> read32 rbuf- VersionInfo <$> getVersion <*> replicateM (cnt - 1) getVersion+ | len < 3 || remainder /= 0 = Just brokenVersionInfo+ | otherwise = Just $+ unsafeDupablePerformIO $+ withReadBuffer bs $ \rbuf -> do+ let getVersion = Version <$> read32 rbuf+ VersionInfo <$> getVersion <*> replicateM (cnt - 1) getVersion where len = BS.length bs- (cnt,remainder) = len `divMod` 4+ (cnt, remainder) = len `divMod` 4 fromParameterList :: ParameterList -> Parameters fromParameterList kvs = foldl' update params kvs where params = baseParameters- update x (OriginalDestinationConnectionId,v)- = x { originalDestinationConnectionId = Just (toCID v) }- update x (MaxIdleTimeout,v)- = x { maxIdleTimeout = decMilliseconds v }- update x (StateLessResetToken,v)- = x { statelessResetToken = Just (StatelessResetToken $ Short.toShort v) }- update x (MaxUdpPayloadSize,v)- = x { maxUdpPayloadSize = decInt v }- update x (InitialMaxData,v)- = x { initialMaxData = decInt v }- update x (InitialMaxStreamDataBidiLocal,v)- = x { initialMaxStreamDataBidiLocal = decInt v }- update x (InitialMaxStreamDataBidiRemote,v)- = x { initialMaxStreamDataBidiRemote = decInt v }- update x (InitialMaxStreamDataUni,v)- = x { initialMaxStreamDataUni = decInt v }- update x (InitialMaxStreamsBidi,v)- = x { initialMaxStreamsBidi = decInt v }- update x (InitialMaxStreamsUni,v)- = x { initialMaxStreamsUni = decInt v }- update x (AckDelayExponent,v)- = x { ackDelayExponent = decInt v }- update x (MaxAckDelay,v)- = x { maxAckDelay = decMilliseconds v }- update x (DisableActiveMigration,_)- = x { disableActiveMigration = True }- update x (PreferredAddress,v)- = x { preferredAddress = Just v }- update x (ActiveConnectionIdLimit,v)- = x { activeConnectionIdLimit = decInt v }- update x (InitialSourceConnectionId,v)- = x { initialSourceConnectionId = Just (toCID v) }- update x (RetrySourceConnectionId,v)- = x { retrySourceConnectionId = Just (toCID v) }- update x (Grease,v)- = x { grease = Just v }- update x (GreaseQuicBit,_)- = x { greaseQuicBit = True }- update x (VersionInformation,v)- = x { versionInformation = toVersionInfo v }+ update x (OriginalDestinationConnectionId, v) =+ x{originalDestinationConnectionId = Just (toCID v)}+ update x (MaxIdleTimeout, v) =+ x{maxIdleTimeout = decMilliseconds v}+ update x (StateLessResetToken, v) =+ x{statelessResetToken = Just (StatelessResetToken $ Short.toShort v)}+ update x (MaxUdpPayloadSize, v) =+ x{maxUdpPayloadSize = decInt v}+ update x (InitialMaxData, v) =+ x{initialMaxData = decInt v}+ update x (InitialMaxStreamDataBidiLocal, v) =+ x{initialMaxStreamDataBidiLocal = decInt v}+ update x (InitialMaxStreamDataBidiRemote, v) =+ x{initialMaxStreamDataBidiRemote = decInt v}+ update x (InitialMaxStreamDataUni, v) =+ x{initialMaxStreamDataUni = decInt v}+ update x (InitialMaxStreamsBidi, v) =+ x{initialMaxStreamsBidi = decInt v}+ update x (InitialMaxStreamsUni, v) =+ x{initialMaxStreamsUni = decInt v}+ update x (AckDelayExponent, v) =+ x{ackDelayExponent = decInt v}+ update x (MaxAckDelay, v) =+ x{maxAckDelay = decMilliseconds v}+ update x (DisableActiveMigration, _) =+ x{disableActiveMigration = True}+ update x (PreferredAddress, v) =+ x{preferredAddress = Just v}+ update x (ActiveConnectionIdLimit, v) =+ x{activeConnectionIdLimit = decInt v}+ update x (InitialSourceConnectionId, v) =+ x{initialSourceConnectionId = Just (toCID v)}+ update x (RetrySourceConnectionId, v) =+ x{retrySourceConnectionId = Just (toCID v)}+ update x (Grease, v) =+ x{grease = Just v}+ update x (GreaseQuicBit, _) =+ x{greaseQuicBit = True}+ update x (VersionInformation, v) =+ x{versionInformation = toVersionInfo v} update x _ = x -diff :: Eq a => Parameters -> (Parameters -> a) -> Key -> (a -> Value) -> Maybe (Key,Value)+diff+ :: Eq a+ => Parameters+ -> (Parameters -> a)+ -> Key+ -> (a -> Value)+ -> Maybe (Key, Value) diff params label key enc- | val == val0 = Nothing- | otherwise = Just (key, enc val)+ | val == val0 = Nothing+ | otherwise = Just (key, enc val) where val = label params val0 = label baseParameters toParameterList :: Parameters -> ParameterList-toParameterList p = catMaybes [- diff p originalDestinationConnectionId- OriginalDestinationConnectionId (fromCID . fromJust)- , diff p maxIdleTimeout MaxIdleTimeout encMilliseconds- , diff p statelessResetToken StateLessResetToken encSRT- , diff p maxUdpPayloadSize MaxUdpPayloadSize encInt- , diff p initialMaxData InitialMaxData encInt- , diff p initialMaxStreamDataBidiLocal InitialMaxStreamDataBidiLocal encInt- , diff p initialMaxStreamDataBidiRemote InitialMaxStreamDataBidiRemote encInt- , diff p initialMaxStreamDataUni InitialMaxStreamDataUni encInt- , diff p initialMaxStreamsBidi InitialMaxStreamsBidi encInt- , diff p initialMaxStreamsUni InitialMaxStreamsUni encInt- , diff p ackDelayExponent AckDelayExponent encInt- , diff p maxAckDelay MaxAckDelay encMilliseconds- , diff p disableActiveMigration DisableActiveMigration (const "")- , diff p preferredAddress PreferredAddress fromJust- , diff p activeConnectionIdLimit ActiveConnectionIdLimit encInt- , diff p initialSourceConnectionId- InitialSourceConnectionId (fromCID . fromJust)- , diff p retrySourceConnectionId- RetrySourceConnectionId (fromCID . fromJust)- , diff p greaseQuicBit GreaseQuicBit (const "")- , diff p grease Grease fromJust- , diff p versionInformation VersionInformation fromVersionInfo- ]+toParameterList p =+ catMaybes+ [ diff+ p+ originalDestinationConnectionId+ OriginalDestinationConnectionId+ (fromCID . fromJust)+ , diff p maxIdleTimeout MaxIdleTimeout encMilliseconds+ , diff p statelessResetToken StateLessResetToken encSRT+ , diff p maxUdpPayloadSize MaxUdpPayloadSize encInt+ , diff p initialMaxData InitialMaxData encInt+ , diff p initialMaxStreamDataBidiLocal InitialMaxStreamDataBidiLocal encInt+ , diff p initialMaxStreamDataBidiRemote InitialMaxStreamDataBidiRemote encInt+ , diff p initialMaxStreamDataUni InitialMaxStreamDataUni encInt+ , diff p initialMaxStreamsBidi InitialMaxStreamsBidi encInt+ , diff p initialMaxStreamsUni InitialMaxStreamsUni encInt+ , diff p ackDelayExponent AckDelayExponent encInt+ , diff p maxAckDelay MaxAckDelay encMilliseconds+ , diff p disableActiveMigration DisableActiveMigration (const "")+ , diff p preferredAddress PreferredAddress fromJust+ , diff p activeConnectionIdLimit ActiveConnectionIdLimit encInt+ , diff+ p+ initialSourceConnectionId+ InitialSourceConnectionId+ (fromCID . fromJust)+ , diff+ p+ retrySourceConnectionId+ RetrySourceConnectionId+ (fromCID . fromJust)+ , diff p greaseQuicBit GreaseQuicBit (const "")+ , diff p grease Grease fromJust+ , diff p versionInformation VersionInformation fromVersionInfo+ ] encSRT :: Maybe StatelessResetToken -> ByteString encSRT (Just (StatelessResetToken srt)) = Short.fromShort srt@@ -243,10 +263,11 @@ encodeParameterList :: ParameterList -> ByteString encodeParameterList kvs = unsafeDupablePerformIO $- withWriteBuffer 4096 $ \wbuf -> do -- for grease+ withWriteBuffer 4096 $ \wbuf -> do+ -- for grease mapM_ (put wbuf) kvs where- put wbuf (Key k,v) = do+ put wbuf (Key k, v) = do encodeInt' wbuf $ fromIntegral k encodeInt' wbuf $ fromIntegral $ BS.length v copyByteString wbuf v@@ -255,49 +276,53 @@ decodeParameterList bs = unsafeDupablePerformIO $ withReadBuffer bs (`go` id) where go rbuf build = do- rest1 <- remainingSize rbuf- if rest1 == 0 then- return $ Just (build [])- else do- key <- fromIntegral <$> decodeInt' rbuf- len <- fromIntegral <$> decodeInt' rbuf- val <- extractByteString rbuf len- go rbuf (build . ((Key key,val):))+ rest1 <- remainingSize rbuf+ if rest1 == 0+ then return $ Just (build [])+ else do+ key <- fromIntegral <$> decodeInt' rbuf+ len <- fromIntegral <$> decodeInt' rbuf+ val <- extractByteString rbuf len+ go rbuf (build . ((Key key, val) :)) -- | An example parameters obsoleted in the near future. defaultParameters :: Parameters-defaultParameters = baseParameters {- maxIdleTimeout = microToMilli idleTimeout -- 30000- , maxUdpPayloadSize = maximumUdpPayloadSize -- 2048- , initialMaxData = 1048576- , initialMaxStreamDataBidiLocal = 262144- , initialMaxStreamDataBidiRemote = 262144- , initialMaxStreamDataUni = 262144- , initialMaxStreamsBidi = 100- , initialMaxStreamsUni = 3- , activeConnectionIdLimit = 3- , greaseQuicBit = True- }+defaultParameters =+ baseParameters+ { maxIdleTimeout = microToMilli idleTimeout -- 30000+ , maxUdpPayloadSize = maximumUdpPayloadSize -- 2048+ , initialMaxData = 1048576+ , initialMaxStreamDataBidiLocal = 262144+ , initialMaxStreamDataBidiRemote = 262144+ , initialMaxStreamDataUni = 262144+ , initialMaxStreamsBidi = 100+ , initialMaxStreamsUni = 3+ , activeConnectionIdLimit = 3+ , greaseQuicBit = True+ } -data AuthCIDs = AuthCIDs {- initSrcCID :: Maybe CID- , origDstCID :: Maybe CID- , retrySrcCID :: Maybe CID- } deriving (Eq, Show)+data AuthCIDs = AuthCIDs+ { initSrcCID :: Maybe CID+ , origDstCID :: Maybe CID+ , retrySrcCID :: Maybe CID+ }+ deriving (Eq, Show) defaultAuthCIDs :: AuthCIDs defaultAuthCIDs = AuthCIDs Nothing Nothing Nothing setCIDsToParameters :: AuthCIDs -> Parameters -> Parameters-setCIDsToParameters AuthCIDs{..} params = params {- originalDestinationConnectionId = origDstCID- , initialSourceConnectionId = initSrcCID- , retrySourceConnectionId = retrySrcCID- }+setCIDsToParameters AuthCIDs{..} params =+ params+ { originalDestinationConnectionId = origDstCID+ , initialSourceConnectionId = initSrcCID+ , retrySourceConnectionId = retrySrcCID+ } getCIDsToParameters :: Parameters -> AuthCIDs-getCIDsToParameters Parameters{..} = AuthCIDs {- origDstCID = originalDestinationConnectionId- , initSrcCID = initialSourceConnectionId- , retrySrcCID = retrySourceConnectionId- }+getCIDsToParameters Parameters{..} =+ AuthCIDs+ { origDstCID = originalDestinationConnectionId+ , initSrcCID = initialSourceConnectionId+ , retrySrcCID = retrySourceConnectionId+ }
Network/QUIC/QLogger.hs view
@@ -1,9 +1,9 @@ {-# LANGUAGE OverloadedStrings #-} module Network.QUIC.QLogger (- QLogger- , dirQLogger- ) where+ QLogger,+ dirQLogger,+) where import System.FilePath import System.Log.FastLogger@@ -12,7 +12,8 @@ import Network.QUIC.Qlog import Network.QUIC.Types -dirQLogger :: Maybe FilePath -> TimeMicrosecond -> CID -> ByteString -> IO (QLogger, IO ())+dirQLogger+ :: Maybe FilePath -> TimeMicrosecond -> CID -> ByteString -> IO (QLogger, IO ()) dirQLogger Nothing _ _ _ = do let qLog ~_ = return () clean = return ()
Network/QUIC/Qlog.hs view
@@ -3,23 +3,23 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Qlog (- QLogger- , newQlogger- , Qlog(..)- , KeepQlog(..)- , QlogMsg(..)- , qlogReceived- , qlogDropped- , qlogRecvInitial- , qlogSentRetry- , qlogParamsSet- , qlogDebug- , qlogCIDUpdate- , Debug(..)- , LR(..)- , packetType- , sw- ) where+ QLogger,+ newQlogger,+ Qlog (..),+ KeepQlog (..),+ QlogMsg (..),+ qlogReceived,+ qlogDropped,+ qlogRecvInitial,+ qlogSentRetry,+ qlogParamsSet,+ qlogDebug,+ qlogCIDUpdate,+ Debug (..),+ LR (..),+ packetType,+ sw,+) where import qualified Data.ByteString as BS @@ -44,14 +44,15 @@ qlog (Debug msg) = "{\"message\":\"" <> msg <> "\"}" instance Qlog LR where- qlog (Local cid) = "{\"owner\":\"local\",\"new\":\"" <> sw cid <> "\"}"+ qlog (Local cid) = "{\"owner\":\"local\",\"new\":\"" <> sw cid <> "\"}" qlog (Remote cid) = "{\"owner\":\"remote\",\"new\":\"" <> sw cid <> "\"}" instance Qlog RetryPacket where qlog RetryPacket{} = "{\"header\":{\"packet_type\":\"retry\",\"packet_number\":\"\"}}" instance Qlog VersionNegotiationPacket where- qlog VersionNegotiationPacket{} = "{\"header\":{\"packet_type\":\"version_negotiation\",\"packet_number\":\"\"}}"+ qlog VersionNegotiationPacket{} =+ "{\"header\":{\"packet_type\":\"version_negotiation\",\"packet_number\":\"\"}}" instance Qlog Header where qlog hdr = "{\"header\":{\"packet_type\":\"" <> packetType hdr <> "\"}}"@@ -60,90 +61,126 @@ qlog (CryptPacket hdr _) = qlog hdr instance Qlog PlainPacket where- qlog (PlainPacket hdr Plain{..}) = "{\"header\":{\"packet_type\":\"" <> toLogStr (packetType hdr) <> "\",\"packet_number\":\"" <> sw plainPacketNumber <> "\",\"dcid\":\"" <> sw (headerMyCID hdr) <> "\"},\"frames\":[" <> foldr (<>) "" (intersperse "," (map qlog plainFrames)) <> "]}"+ qlog (PlainPacket hdr Plain{..}) =+ "{\"header\":{\"packet_type\":\""+ <> toLogStr (packetType hdr)+ <> "\",\"packet_number\":\""+ <> sw plainPacketNumber+ <> "\",\"dcid\":\""+ <> sw (headerMyCID hdr)+ <> "\"},\"frames\":["+ <> foldr (<>) "" (intersperse "," (map qlog plainFrames))+ <> "]}" instance Qlog StatelessReset where qlog StatelessReset = "{\"header\":{\"packet_type\":\"stateless_reset\",\"packet_number\":\"\"}}" packetType :: Header -> LogStr-packetType Initial{} = "initial"-packetType RTT0{} = "0RTT"+packetType Initial{} = "initial"+packetType RTT0{} = "0RTT" packetType Handshake{} = "handshake"-packetType Short{} = "1RTT"+packetType Short{} = "1RTT" instance Qlog Frame where qlog frame = "{\"frame_type\":\"" <> frameType frame <> "\"" <> frameExtra frame <> "}" frameType :: Frame -> LogStr-frameType Padding{} = "padding"-frameType Ping = "ping"-frameType Ack{} = "ack"-frameType ResetStream{} = "reset_stream"-frameType StopSending{} = "stop_sending"-frameType CryptoF{} = "crypto"-frameType NewToken{} = "new_token"-frameType StreamF{} = "stream"-frameType MaxData{} = "max_data"-frameType MaxStreamData{} = "max_stream_data"-frameType MaxStreams{} = "max_streams"-frameType DataBlocked{} = "data_blocked"-frameType StreamDataBlocked{} = "stream_data_blocked"-frameType StreamsBlocked{} = "streams_blocked"-frameType NewConnectionID{} = "new_connection_id"-frameType RetireConnectionID{} = "retire_connection_id"-frameType PathChallenge{} = "path_challenge"-frameType PathResponse{} = "path_response"-frameType ConnectionClose{} = "connection_close"-frameType ConnectionCloseApp{} = "connection_close"-frameType HandshakeDone{} = "handshake_done"-frameType UnknownFrame{} = "unknown"+frameType Padding{} = "padding"+frameType Ping = "ping"+frameType Ack{} = "ack"+frameType ResetStream{} = "reset_stream"+frameType StopSending{} = "stop_sending"+frameType CryptoF{} = "crypto"+frameType NewToken{} = "new_token"+frameType StreamF{} = "stream"+frameType MaxData{} = "max_data"+frameType MaxStreamData{} = "max_stream_data"+frameType MaxStreams{} = "max_streams"+frameType DataBlocked{} = "data_blocked"+frameType StreamDataBlocked{} = "stream_data_blocked"+frameType StreamsBlocked{} = "streams_blocked"+frameType NewConnectionID{} = "new_connection_id"+frameType RetireConnectionID{} = "retire_connection_id"+frameType PathChallenge{} = "path_challenge"+frameType PathResponse{} = "path_response"+frameType ConnectionClose{} = "connection_close"+frameType ConnectionCloseApp{} = "connection_close"+frameType HandshakeDone{} = "handshake_done"+frameType UnknownFrame{} = "unknown" {-# INLINE frameExtra #-} frameExtra :: Frame -> LogStr frameExtra (Padding n) = ",\"payload_length\":" <> sw n-frameExtra Ping = ""+frameExtra Ping = "" frameExtra (Ack ai _Delay) = ",\"acked_ranges\":" <> ack ai frameExtra ResetStream{} = "" frameExtra (StopSending _StreamId _ApplicationError) = ""-frameExtra (CryptoF off dat) = ",\"offset\":\"" <> sw off <> "\",\"length\":" <> sw (BS.length dat)+frameExtra (CryptoF off dat) = ",\"offset\":\"" <> sw off <> "\",\"length\":" <> sw (BS.length dat) frameExtra (NewToken _Token) = ""-frameExtra (StreamF sid off dat fin) = ",\"stream_id\":\"" <> sw sid <> "\",\"offset\":\"" <> sw off <> "\",\"length\":" <> sw (sum' $ map BS.length dat) <> ",\"fin\":" <> if fin then "true" else "false"+frameExtra (StreamF sid off dat fin) =+ ",\"stream_id\":\""+ <> sw sid+ <> "\",\"offset\":\""+ <> sw off+ <> "\",\"length\":"+ <> sw (sum' $ map BS.length dat)+ <> ",\"fin\":"+ <> if fin then "true" else "false" frameExtra (MaxData mx) = ",\"maximum\":\"" <> sw mx <> "\"" frameExtra (MaxStreamData sid mx) = ",\"stream_id\":\"" <> sw sid <> "\",\"maximum\":\"" <> sw mx <> "\"" frameExtra (MaxStreams _Direction ms) = ",\"maximum\":\"" <> sw ms <> "\"" frameExtra DataBlocked{} = "" frameExtra StreamDataBlocked{} = "" frameExtra StreamsBlocked{} = ""-frameExtra (NewConnectionID (CIDInfo sn cid _) rpt) = ",\"sequence_number\":\"" <> sw sn <> "\",\"connection_id:\":\"" <> sw cid <> "\",\"retire_prior_to\":\"" <> sw rpt <> "\""+frameExtra (NewConnectionID (CIDInfo sn cid _) rpt) =+ ",\"sequence_number\":\""+ <> sw sn+ <> "\",\"connection_id:\":\""+ <> sw cid+ <> "\",\"retire_prior_to\":\""+ <> sw rpt+ <> "\"" frameExtra (RetireConnectionID sn) = ",\"sequence_number\":\"" <> sw sn <> "\"" frameExtra (PathChallenge _PathData) = "" frameExtra (PathResponse _PathData) = ""-frameExtra (ConnectionClose err _FrameType reason) = ",\"error_space\":\"transport\",\"error_code\":\"" <> transportError err <> "\",\"raw_error_code\":" <> transportError' err <> ",\"reason\":\"" <> toLogStr (Short.fromShort reason) <> "\""-frameExtra (ConnectionCloseApp err reason) = ",\"error_space\":\"application\",\"error_code\":" <> applicationProtoclError err <> ",\"reason\":\"" <> toLogStr (Short.fromShort reason) <> "\"" -- fixme+frameExtra (ConnectionClose err _FrameType reason) =+ ",\"error_space\":\"transport\",\"error_code\":\""+ <> transportError err+ <> "\",\"raw_error_code\":"+ <> transportError' err+ <> ",\"reason\":\""+ <> toLogStr (Short.fromShort reason)+ <> "\""+frameExtra (ConnectionCloseApp err reason) =+ ",\"error_space\":\"application\",\"error_code\":"+ <> applicationProtoclError err+ <> ",\"reason\":\""+ <> toLogStr (Short.fromShort reason)+ <> "\"" -- fixme frameExtra HandshakeDone{} = "" frameExtra (UnknownFrame _Int) = "" transportError :: TransportError -> LogStr-transportError NoError = "no_error"-transportError InternalError = "internal_error"-transportError ConnectionRefused = "connection_refused"-transportError FlowControlError = "flow_control_error"-transportError StreamLimitError = "stream_limit_error"-transportError StreamStateError = "stream_state_error"-transportError FinalSizeError = "final_size_error"-transportError FrameEncodingError = "frame_encoding_error"+transportError NoError = "no_error"+transportError InternalError = "internal_error"+transportError ConnectionRefused = "connection_refused"+transportError FlowControlError = "flow_control_error"+transportError StreamLimitError = "stream_limit_error"+transportError StreamStateError = "stream_state_error"+transportError FinalSizeError = "final_size_error"+transportError FrameEncodingError = "frame_encoding_error" transportError TransportParameterError = "transport_parameter_err"-transportError ConnectionIdLimitError = "connection_id_limit_error"-transportError ProtocolViolation = "protocol_violation"-transportError InvalidToken = "invalid_migration"-transportError CryptoBufferExceeded = "crypto_buffer_exceeded"-transportError KeyUpdateError = "key_update_error"-transportError AeadLimitReached = "aead_limit_reached"-transportError NoViablePath = "no_viablpath"-transportError (TransportError n) = sw n+transportError ConnectionIdLimitError = "connection_id_limit_error"+transportError ProtocolViolation = "protocol_violation"+transportError InvalidToken = "invalid_migration"+transportError CryptoBufferExceeded = "crypto_buffer_exceeded"+transportError KeyUpdateError = "key_update_error"+transportError AeadLimitReached = "aead_limit_reached"+transportError NoViablePath = "no_viablpath"+transportError (TransportError n) = sw n transportError' :: TransportError -> LogStr-transportError' (TransportError n) = sw n+transportError' (TransportError n) = sw n applicationProtoclError :: ApplicationProtocolError -> LogStr applicationProtoclError (ApplicationProtocolError n) = sw n@@ -153,12 +190,13 @@ ack (AckInfo lpn r rs) = "[" <> ack1 fr fpn rs <> "]" where fpn = fromIntegral lpn - r- fr | r == 0 = "[" <> sw lpn <> "]"- | otherwise = "[" <> sw fpn <> "," <> sw lpn <> "]"+ fr+ | r == 0 = "[" <> sw lpn <> "]"+ | otherwise = "[" <> sw fpn <> "," <> sw lpn <> "]" ack1 :: LogStr -> Range -> [(Gap, Range)] -> LogStr-ack1 ret _ [] = ret-ack1 ret fpn ((g,r):grs) = ack1 ret' f grs+ack1 ret _ [] = ret+ack1 ret fpn ((g, r) : grs) = ack1 ret' f grs where ret' = "[" <> sw f <> "," <> sw l <> "]," <> ret l = fpn - g - 2@@ -166,29 +204,35 @@ ---------------------------------------------------------------- -instance Qlog (Parameters,String) where- qlog (Parameters{..},owner) =- "{\"owner\":\"" <> toLogStr owner- <> "\",\"initial_max_data\":\"" <> sw initialMaxData- <> "\",\"initial_max_stream_data_bidi_local\":\"" <> sw initialMaxStreamDataBidiLocal- <> "\",\"initial_max_stream_data_bidi_remote\":\"" <> sw initialMaxStreamDataBidiRemote- <> "\",\"initial_max_stream_data_uni\":\"" <> sw initialMaxStreamDataUni- <> "\"}"+instance Qlog (Parameters, String) where+ qlog (Parameters{..}, owner) =+ "{\"owner\":\""+ <> toLogStr owner+ <> "\",\"initial_max_data\":\""+ <> sw initialMaxData+ <> "\",\"initial_max_stream_data_bidi_local\":\""+ <> sw initialMaxStreamDataBidiLocal+ <> "\",\"initial_max_stream_data_bidi_remote\":\""+ <> sw initialMaxStreamDataBidiRemote+ <> "\",\"initial_max_stream_data_uni\":\""+ <> sw initialMaxStreamDataUni+ <> "\"}" ---------------------------------------------------------------- -data QlogMsg = QRecvInitial- | QSentRetry- | QSent LogStr TimeMicrosecond- | QReceived LogStr TimeMicrosecond- | QDropped LogStr TimeMicrosecond- | QMetricsUpdated LogStr TimeMicrosecond- | QPacketLost LogStr TimeMicrosecond- | QCongestionStateUpdated LogStr TimeMicrosecond- | QLossTimerUpdated LogStr TimeMicrosecond- | QDebug LogStr TimeMicrosecond- | QParamsSet LogStr TimeMicrosecond- | QCIDUpdate LogStr TimeMicrosecond+data QlogMsg+ = QRecvInitial+ | QSentRetry+ | QSent LogStr TimeMicrosecond+ | QReceived LogStr TimeMicrosecond+ | QDropped LogStr TimeMicrosecond+ | QMetricsUpdated LogStr TimeMicrosecond+ | QPacketLost LogStr TimeMicrosecond+ | QCongestionStateUpdated LogStr TimeMicrosecond+ | QLossTimerUpdated LogStr TimeMicrosecond+ | QDebug LogStr TimeMicrosecond+ | QParamsSet LogStr TimeMicrosecond+ | QCIDUpdate LogStr TimeMicrosecond {-# INLINE toLogStrTime #-} toLogStrTime :: QlogMsg -> TimeMicrosecond -> LogStr@@ -197,25 +241,65 @@ toLogStrTime QSentRetry _ = "{\"time\":0,\"name\":\"transport:packet_sent\",\"data\":{\"header\":{\"packet_type\":\"retry\",\"packet_number\":\"\"}}}\n" toLogStrTime (QReceived msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"transport:packet_received\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"transport:packet_received\",\"data\":"+ <> msg+ <> "}\n" toLogStrTime (QSent msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"transport:packet_sent\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"transport:packet_sent\",\"data\":"+ <> msg+ <> "}\n" toLogStrTime (QDropped msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"transport:packet_dropped\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"transport:packet_dropped\",\"data\":"+ <> msg+ <> "}\n" toLogStrTime (QParamsSet msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"transport:parameters_set\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"transport:parameters_set\",\"data\":"+ <> msg+ <> "}\n" toLogStrTime (QMetricsUpdated msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"recovery:metrics_updated\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"recovery:metrics_updated\",\"data\":"+ <> msg+ <> "}\n" toLogStrTime (QPacketLost msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"recovery:packet_lost\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"recovery:packet_lost\",\"data\":"+ <> msg+ <> "}\n" toLogStrTime (QCongestionStateUpdated msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"recovery:congestion_state_updated\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"recovery:congestion_state_updated\",\"data\":"+ <> msg+ <> "}\n" toLogStrTime (QLossTimerUpdated msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"recovery:loss_timer_updated\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"recovery:loss_timer_updated\",\"data\":"+ <> msg+ <> "}\n" toLogStrTime (QDebug msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"debug\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"debug\",\"data\":"+ <> msg+ <> "}\n" toLogStrTime (QCIDUpdate msg tim) base =- "{\"time\":" <> swtim tim base <> ",\"name\":\"connectivity:connection_id_updated\",\"data\":" <> msg <> "}\n"+ "{\"time\":"+ <> swtim tim base+ <> ",\"name\":\"connectivity:connection_id_updated\",\"data\":"+ <> msg+ <> "}\n" ---------------------------------------------------------------- @@ -228,7 +312,7 @@ swtim tim base = toLogStr (show m ++ "." ++ show u) where Microseconds x = elapsedTimeMicrosecond tim base- (m,u) = x `divMod` 1000+ (m, u) = x `divMod` 1000 ---------------------------------------------------------------- @@ -237,7 +321,16 @@ newQlogger :: TimeMicrosecond -> ByteString -> CID -> FastLogger -> IO QLogger newQlogger base rl ocid fastLogger = do let ocid' = toLogStr $ enc16 $ fromCID ocid- fastLogger $ "{\"qlog_format\":\"NDJSON\",\"qlog_version\":\"draft-02\",\"title\":\"Haskell quic qlog\",\"trace\":{\"vantage_point\":{\"type\":\"" <> toLogStr rl <> "\"},\"common_fields\":{\"ODCID\":\"" <> ocid' <> "\",\"group_id\":\"" <> ocid' <> "\",\"reference_time\":" <> swtim base timeMicrosecond0 <> "}}}\n"+ fastLogger $+ "{\"qlog_format\":\"NDJSON\",\"qlog_version\":\"draft-02\",\"title\":\"Haskell quic qlog\",\"trace\":{\"vantage_point\":{\"type\":\""+ <> toLogStr rl+ <> "\"},\"common_fields\":{\"ODCID\":\""+ <> ocid'+ <> "\",\"group_id\":\""+ <> ocid'+ <> "\",\"reference_time\":"+ <> swtim base timeMicrosecond0+ <> "}}}\n" let qlogger qmsg = do let msg = toLogStrTime qmsg base fastLogger msg@@ -262,7 +355,7 @@ qlogSentRetry :: KeepQlog q => q -> IO () qlogSentRetry q = keepQlog q QSentRetry -qlogParamsSet :: KeepQlog q => q -> (Parameters,String) -> IO ()+qlogParamsSet :: KeepQlog q => q -> (Parameters, String) -> IO () qlogParamsSet q params = do tim <- getTimeMicrosecond keepQlog q $ QParamsSet (qlog params) tim
Network/QUIC/Receiver.hs view
@@ -2,11 +2,12 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Receiver (- receiver- ) where+ receiver,+) where import qualified Data.ByteString as BS-import Network.TLS (AlertDescription(..))+import Network.Control+import Network.TLS (AlertDescription (..)) import UnliftIO.Concurrent (forkIO) import qualified UnliftIO.Exception as E @@ -23,7 +24,7 @@ import Network.QUIC.Recovery import Network.QUIC.Server.Reader (runNewServerReader) import Network.QUIC.Stream-import Network.QUIC.Types+import Network.QUIC.Types as QUIC receiver :: Connection -> IO () receiver conn = handleLogT logAction body@@ -37,13 +38,14 @@ ito <- readMinIdleTimeout conn mx <- timeout ito "recvTimeout" $ connRecv conn -- fixme: taking minimum with peer's one case mx of- Nothing -> do- st <- getConnectionState conn- let msg0 | isClient conn = "Client"- | otherwise = "Server"- msg = msg0 ++ " " ++ show st- E.throwIO $ ConnectionIsTimeout msg- Just x -> return x+ Nothing -> do+ st <- getConnectionState conn+ let msg0+ | isClient conn = "Client"+ | otherwise = "Server"+ msg = msg0 ++ " " ++ show st+ E.throwIO $ ConnectionIsTimeout msg+ Just x -> return x loopHandshake = do rpkt <- recvTimeout processReceivedPacketHandshake conn rpkt@@ -55,70 +57,88 @@ cid = headerMyCID hdr included <- myCIDsInclude conn cid case included of- Just nseq -> do- shouldUpdate <- shouldUpdateMyCID conn nseq- when shouldUpdate $ do- setMyCID conn cid- cidInfo <- getNewMyCID conn- when (isServer conn) $ do- register <- getRegister conn- register (cidInfoCID cidInfo) conn- sendFrames conn RTT1Level [NewConnectionID cidInfo 0]- processReceivedPacket conn rpkt- shouldUpdatePeer <- if shouldUpdate then shouldUpdatePeerCID conn- else return False- when shouldUpdatePeer $ choosePeerCIDForPrivacy conn- _ -> do- qlogDropped conn hdr- connDebugLog conn $ bhow cid <> " is unknown"+ Just nseq -> do+ shouldUpdate <- shouldUpdateMyCID conn nseq+ when shouldUpdate $ do+ setMyCID conn cid+ cidInfo <- getNewMyCID conn+ when (isServer conn) $ do+ register <- getRegister conn+ register (cidInfoCID cidInfo) conn+ sendFrames conn RTT1Level [NewConnectionID cidInfo 0]+ processReceivedPacket conn rpkt+ shouldUpdatePeer <-+ if shouldUpdate+ then shouldUpdatePeerCID conn+ else return False+ when shouldUpdatePeer $ choosePeerCIDForPrivacy conn+ _ -> do+ qlogDropped conn hdr+ connDebugLog conn $ bhow cid <> " is unknown" logAction msg = connDebugLog conn ("debug: receiver: " <> msg) processReceivedPacketHandshake :: Connection -> ReceivedPacket -> IO () processReceivedPacketHandshake conn rpkt = do let CryptPacket hdr _ = rpCryptPacket rpkt lvl = rpEncryptionLevel rpkt- msg = "processReceivedPacketHandshake " ++ if isServer conn then "Server" else "Client"+ msg =+ "processReceivedPacketHandshake "+ ++ if isServer conn then "Server" else "Client" mx <- timeout (Microseconds 10000) msg $ waitEncryptionLevel conn lvl case mx of- Nothing -> do- putOffCrypto conn lvl rpkt- when (isClient conn) $ do- lvl' <- getEncryptionLevel conn- speedup (connLDCC conn) lvl' "not decryptable"- Just ()- | isClient conn -> do- when (lvl == InitialLevel) $ do- peercid <- getPeerCID conn- let newPeerCID = headerPeerCID hdr- when (peercid /= headerPeerCID hdr) $- resetPeerCID conn newPeerCID- setPeerAuthCIDs conn $ \auth ->- auth { initSrcCID = Just newPeerCID }- case hdr of- Initial peerVer _ _ _ -> do- myVer <- getVersion conn- let myOrigiVer = getOriginalVersion conn- firstTime = myVer == myOrigiVer- when (firstTime && myVer /= peerVer) $ do- setVersion conn peerVer- dcid <- getClientDstCID conn- initializeCoder conn InitialLevel $ initialSecrets peerVer dcid- _ -> return ()- processReceivedPacket conn rpkt- | otherwise -> do- mycid <- getMyCID conn- when (lvl == HandshakeLevel- || (lvl == InitialLevel && mycid == headerMyCID hdr)) $ do- setAddressValidated conn- when (lvl == HandshakeLevel) $ do- let ldcc = connLDCC conn- discarded <- getAndSetPacketNumberSpaceDiscarded ldcc InitialLevel- unless discarded $ fire conn (Microseconds 100000) $ do- dropSecrets conn InitialLevel- clearCryptoStream conn InitialLevel- onPacketNumberSpaceDiscarded ldcc InitialLevel- processReceivedPacket conn rpkt+ Nothing -> do+ putOffCrypto conn lvl rpkt+ when (isClient conn) $ do+ lvl' <- getEncryptionLevel conn+ speedup (connLDCC conn) lvl' "not decryptable"+ Just ()+ | isClient conn -> do+ when (lvl == InitialLevel) $ do+ peercid <- getPeerCID conn+ let newPeerCID = headerPeerCID hdr+ when (peercid /= headerPeerCID hdr) $+ resetPeerCID conn newPeerCID+ setPeerAuthCIDs conn $ \auth ->+ auth{initSrcCID = Just newPeerCID}+ case hdr of+ Initial peerVer _ _ _ -> do+ myVer <- getVersion conn+ let myOrigiVer = getOriginalVersion conn+ firstTime = myVer == myOrigiVer+ when (firstTime && myVer /= peerVer) $ do+ setVersion conn peerVer+ dcid <- getClientDstCID conn+ initializeCoder conn InitialLevel $ initialSecrets peerVer dcid+ _ -> return ()+ processReceivedPacket conn rpkt+ | otherwise -> do+ mycid <- getMyCID conn+ when+ ( lvl == HandshakeLevel+ || (lvl == InitialLevel && mycid == headerMyCID hdr)+ )+ $ do+ setAddressValidated conn+ when (lvl == HandshakeLevel) $ do+ let ldcc = connLDCC conn+ discarded <- getAndSetPacketNumberSpaceDiscarded ldcc InitialLevel+ unless discarded $ fire conn (Microseconds 100000) $ do+ dropSecrets conn InitialLevel+ clearCryptoStream conn InitialLevel+ onPacketNumberSpaceDiscarded ldcc InitialLevel+ processReceivedPacket conn rpkt +rateLimit :: Int+rateLimit = 10++checkRate :: [Frame] -> Int+checkRate fs0 = go fs0 0+ where+ go [] n = n+ go (f : fs) n+ | rateControled f = go fs (n + 1)+ | otherwise = go fs n+ processReceivedPacket :: Connection -> ReceivedPacket -> IO () processReceivedPacket conn rpkt = do let CryptPacket hdr crypt = rpCryptPacket rpkt@@ -126,69 +146,82 @@ tim = rpTimeRecevied rpkt mplain <- decryptCrypt conn crypt lvl case mplain of- Just plain@Plain{..} -> do- addRxBytes conn $ rpReceivedBytes rpkt- when (isIllegalReservedBits plainMarks || isNoFrames plainMarks) $- closeConnection ProtocolViolation "Non 0 RR bits or no frames"- when (isUnknownFrame plainMarks) $- closeConnection FrameEncodingError "Unknown frame"- -- For Ping, record PPN first, then send an ACK.- onPacketReceived (connLDCC conn) lvl plainPacketNumber- when (lvl == RTT1Level) $ setPeerPacketNumber conn plainPacketNumber- qlogReceived conn (PlainPacket hdr plain) tim- let ackEli = any ackEliciting plainFrames- case cryptMigraionInfo crypt of- Nothing -> return ()- Just miginfo ->- void . forkIO $ runNewServerReader conn miginfo- (ckp,cpn) <- getCurrentKeyPhase conn- let Flags flags = plainFlags- nkp = flags `testBit` 2- when (nkp /= ckp && plainPacketNumber > cpn) $ do- setCurrentKeyPhase conn nkp plainPacketNumber- updateCoder1RTT conn ckp -- ckp is now next- mapM_ (processFrame conn lvl) plainFrames- when ackEli $ do- case lvl of- RTT0Level -> return ()- RTT1Level -> delayedAck conn- _ -> do- sup <- getSpeedingUp (connLDCC conn)- when sup $ do- qlogDebug conn $ Debug "ping for speedup"- sendFrames conn lvl [Ping]- Nothing -> do- statelessReset <- isStatelessReset conn hdr crypt- if statelessReset then do- qlogReceived conn StatelessReset tim- connDebugLog conn "debug: connection is reset statelessly"- E.throwIO ConnectionIsReset- else do- qlogDropped conn hdr- connDebugLog conn $ "debug: cannot decrypt: " <> bhow lvl <> " size = " <> bhow (BS.length $ cryptPacket crypt)- -- fixme: sending statelss reset+ Just plain@Plain{..} -> do+ addRxBytes conn $ rpReceivedBytes rpkt+ when (isIllegalReservedBits plainMarks || isNoFrames plainMarks) $+ closeConnection ProtocolViolation "Non 0 RR bits or no frames"+ when (isUnknownFrame plainMarks) $+ closeConnection FrameEncodingError "Unknown frame"+ let controlled = checkRate plainFrames+ when (controlled /= 0) $ do+ rate <- addRate (controlRate conn) controlled+ when (rate > rateLimit) $ do+ closeConnection QUIC.InternalError "Rate control"+ -- For Ping, record PPN first, then send an ACK.+ onPacketReceived (connLDCC conn) lvl plainPacketNumber+ when (lvl == RTT1Level) $ setPeerPacketNumber conn plainPacketNumber+ qlogReceived conn (PlainPacket hdr plain) tim+ let ackEli = any ackEliciting plainFrames+ case cryptMigraionInfo crypt of+ Nothing -> return ()+ Just miginfo ->+ void . forkIO $ runNewServerReader conn miginfo+ (ckp, cpn) <- getCurrentKeyPhase conn+ let Flags flags = plainFlags+ nkp = flags `testBit` 2+ when (nkp /= ckp && plainPacketNumber > cpn) $ do+ setCurrentKeyPhase conn nkp plainPacketNumber+ updateCoder1RTT conn ckp -- ckp is now next+ mapM_ (processFrame conn lvl) plainFrames+ when ackEli $ do+ case lvl of+ RTT0Level -> return ()+ RTT1Level -> delayedAck conn+ _ -> do+ sup <- getSpeedingUp (connLDCC conn)+ when sup $ do+ qlogDebug conn $ Debug "ping for speedup"+ sendFrames conn lvl [Ping]+ Nothing -> do+ statelessReset <- isStatelessReset conn hdr crypt+ if statelessReset+ then do+ qlogReceived conn StatelessReset tim+ connDebugLog conn "debug: connection is reset statelessly"+ E.throwIO ConnectionIsReset+ else do+ qlogDropped conn hdr+ connDebugLog conn $+ "debug: cannot decrypt: "+ <> bhow lvl+ <> " size = "+ <> bhow (BS.length $ cryptPacket crypt) +-- fixme: sending statelss reset+ isSendOnly :: Connection -> StreamId -> Bool isSendOnly conn sid- | isClient conn = isClientInitiatedUnidirectional sid- | otherwise = isServerInitiatedUnidirectional sid+ | isClient conn = isClientInitiatedUnidirectional sid+ | otherwise = isServerInitiatedUnidirectional sid isReceiveOnly :: Connection -> StreamId -> Bool isReceiveOnly conn sid- | isClient conn = isServerInitiatedUnidirectional sid- | otherwise = isClientInitiatedUnidirectional sid+ | isClient conn = isServerInitiatedUnidirectional sid+ | otherwise = isClientInitiatedUnidirectional sid isInitiated :: Connection -> StreamId -> Bool isInitiated conn sid- | isClient conn = isClientInitiated sid- | otherwise = isServerInitiated sid+ | isClient conn = isClientInitiated sid+ | otherwise = isServerInitiated sid guardStream :: Connection -> StreamId -> Maybe Stream -> IO () guardStream conn sid Nothing- | isInitiated conn sid = do+ | isInitiated conn sid = do curSid <- getMyStreamId conn when (sid > curSid) $- closeConnection StreamStateError "a locally-initiated stream that has not yet been created"+ closeConnection+ StreamStateError+ "a locally-initiated stream that has not yet been created" guardStream _ _ _ = return () processFrame :: Connection -> EncryptionLevel -> Frame -> IO ()@@ -206,12 +239,12 @@ closeConnection StreamStateError "Received in a send-only stream" mstrm <- findStream conn sid case mstrm of- Nothing -> return ()- Just strm -> do- onResetStreamReceived (connHooks conn) strm aerr- setTxStreamClosed strm- setRxStreamClosed strm- delStream conn strm+ Nothing -> return ()+ Just strm -> do+ onResetStreamReceived (connHooks conn) strm aerr+ setTxStreamClosed strm+ setRxStreamClosed strm+ delStream conn strm processFrame conn lvl (StopSending sid err) = do when (lvl == InitialLevel || lvl == HandshakeLevel) $ closeConnection ProtocolViolation "STOP_SENDING"@@ -219,10 +252,10 @@ closeConnection StreamStateError "Receive-only stream" mstrm <- findStream conn sid case mstrm of- Nothing -> do- when (isInitiated conn sid) $- closeConnection StreamStateError "No such stream for STOP_SENDING"- Just _strm -> sendFrames conn lvl [ResetStream sid err 0]+ Nothing -> do+ when (isInitiated conn sid) $+ closeConnection StreamStateError "No such stream for STOP_SENDING"+ Just _strm -> sendFrames conn lvl [ResetStream sid err 0] processFrame _ _ (CryptoF _ "") = return () processFrame conn lvl (CryptoF off cdat) = do when (lvl == RTT0Level) $@@ -230,24 +263,24 @@ let len = BS.length cdat rx = RxStreamData cdat off len False case lvl of- InitialLevel -> do- dup <- putRxCrypto conn lvl rx- when dup $ speedup (connLDCC conn) lvl "duplicated"- RTT0Level -> do- connDebugLog conn $ "processFrame: invalid packet type " <> bhow lvl- HandshakeLevel -> do- dup <- putRxCrypto conn lvl rx- when dup $ speedup (connLDCC conn) lvl "duplicated"- RTT1Level- | isClient conn ->- void $ putRxCrypto conn lvl rx- | otherwise ->- closeConnection (cryptoError UnexpectedMessage) "CRYPTO in 1-RTT"+ InitialLevel -> do+ dup <- putRxCrypto conn lvl rx+ when dup $ speedup (connLDCC conn) lvl "duplicated"+ RTT0Level -> do+ connDebugLog conn $ "processFrame: invalid packet type " <> bhow lvl+ HandshakeLevel -> do+ dup <- putRxCrypto conn lvl rx+ when dup $ speedup (connLDCC conn) lvl "duplicated"+ RTT1Level+ | isClient conn ->+ void $ putRxCrypto conn lvl rx+ | otherwise ->+ closeConnection (cryptoError UnexpectedMessage) "CRYPTO in 1-RTT" processFrame conn lvl (NewToken token) = do when (isServer conn || lvl /= RTT1Level) $ closeConnection ProtocolViolation "NEW_TOKEN for server or in 1-RTT" when (isClient conn) $ setNewToken conn token-processFrame conn RTT0Level (StreamF sid off (dat:_) fin) = do+processFrame conn RTT0Level (StreamF sid off (dat : _) fin) = do when (off == 0) $ updatePeerStreamId conn sid -- FLOW CONTROL: MAX_STREAMS: recv: rejecting if over my limit ok <- checkRxMaxStreams conn sid@@ -261,13 +294,14 @@ rx = RxStreamData dat off len fin fc <- putRxStreamData strm rx case fc of- -- FLOW CONTROL: MAX_STREAM_DATA: recv: rejecting if over my limit- OverLimit -> closeConnection FlowControlError "Flow control error for stream in 0-RTT"- Duplicated -> return ()- Reassembled -> do- ok' <- checkRxMaxData conn len- -- FLOW CONTROL: MAX_DATA: send: respecting peer's limit- unless ok' $ closeConnection FlowControlError "Flow control error for connection in 0-RTT"+ -- FLOW CONTROL: MAX_STREAM_DATA: recv: rejecting if over my limit+ OverLimit -> closeConnection FlowControlError "Flow control error for stream in 0-RTT"+ Duplicated -> return ()+ Reassembled -> do+ ok' <- checkRxMaxData conn len+ -- FLOW CONTROL: MAX_DATA: send: respecting peer's limit+ unless ok' $+ closeConnection FlowControlError "Flow control error for connection in 0-RTT" processFrame conn RTT1Level (StreamF sid _ [""] False) = do -- FLOW CONTROL: MAX_STREAMS: recv: rejecting if over my limit ok <- checkRxMaxStreams conn sid@@ -276,7 +310,7 @@ closeConnection StreamStateError "send-only stream" mstrm <- findStream conn sid guardStream conn sid mstrm-processFrame conn RTT1Level (StreamF sid off (dat:_) fin) = do+processFrame conn RTT1Level (StreamF sid off (dat : _) fin) = do when (off == 0) $ updatePeerStreamId conn sid -- FLOW CONTROL: MAX_STREAMS: recv: rejecting if over my limit ok <- checkRxMaxStreams conn sid@@ -290,13 +324,14 @@ rx = RxStreamData dat off len fin fc <- putRxStreamData strm rx case fc of- -- FLOW CONTROL: MAX_STREAM_DATA: recv: rejecting if over my limit- OverLimit -> closeConnection FlowControlError "Flow control error for stream in 1-RTT"- Duplicated -> return ()- Reassembled -> do- ok' <- checkRxMaxData conn len- -- FLOW CONTROL: MAX_DATA: send: respecting peer's limit- unless ok' $ closeConnection FlowControlError "Flow control error for connection in 1-RTT"+ -- FLOW CONTROL: MAX_STREAM_DATA: recv: rejecting if over my limit+ OverLimit -> closeConnection FlowControlError "Flow control error for stream in 1-RTT"+ Duplicated -> return ()+ Reassembled -> do+ ok' <- checkRxMaxData conn len+ -- FLOW CONTROL: MAX_DATA: send: respecting peer's limit+ unless ok' $+ closeConnection FlowControlError "Flow control error for connection in 1-RTT" processFrame conn lvl (MaxData n) = do when (lvl == InitialLevel || lvl == HandshakeLevel) $ closeConnection ProtocolViolation "MAX_DATA in Initial or Handshake"@@ -308,25 +343,24 @@ closeConnection StreamStateError "Receive-only stream" mstrm <- findStream conn sid case mstrm of- Nothing -> do- when (isInitiated conn sid) $- closeConnection StreamStateError "No such stream for MAX_STREAM_DATA"- Just strm -> setTxMaxStreamData strm n+ Nothing -> do+ when (isInitiated conn sid) $+ closeConnection StreamStateError "No such stream for MAX_STREAM_DATA"+ Just strm -> setTxMaxStreamData strm n processFrame conn lvl (MaxStreams dir n) = do when (lvl == InitialLevel || lvl == HandshakeLevel) $ closeConnection ProtocolViolation "MAX_STREAMS in Initial or Handshake"- when (n > 2^(60 :: Int)) $+ when (n > 2 ^ (60 :: Int)) $ closeConnection FrameEncodingError "Too large MAX_STREAMS"- if dir == Bidirectional then- setTxMaxStreams conn n- else- setTxUniMaxStreams conn n+ if dir == Bidirectional+ then setTxMaxStreams conn n+ else setTxUniMaxStreams conn n processFrame _conn _lvl DataBlocked{} = return () processFrame _conn _lvl (StreamDataBlocked _sid _) = return () processFrame _conn lvl (StreamsBlocked _dir n) = do when (lvl == InitialLevel || lvl == HandshakeLevel) $ closeConnection ProtocolViolation "STREAMS_BLOCKED in Initial or Handshake"- when (n > 2^(60 :: Int)) $+ when (n > 2 ^ (60 :: Int)) $ closeConnection FrameEncodingError "Too large STREAMS_BLOCKED" processFrame conn lvl (NewConnectionID cidInfo rpt) = do when (lvl == InitialLevel || lvl == HandshakeLevel) $@@ -341,11 +375,11 @@ processFrame conn RTT1Level (RetireConnectionID sn) = do mcidInfo <- retireMyCID conn sn case mcidInfo of- Nothing -> return ()- Just (CIDInfo _ cid _) -> do- when (isServer conn) $ do- unregister <- getUnregister conn- unregister cid+ Nothing -> return ()+ Just (CIDInfo _ cid _) -> do+ when (isServer conn) $ do+ unregister <- getUnregister conn+ unregister cid processFrame conn RTT1Level (PathChallenge dat) = sendFrames conn RTT1Level [PathResponse dat] processFrame conn RTT1Level (PathResponse dat) =@@ -384,21 +418,21 @@ let cid = headerMyCID hdr included <- myCIDsInclude conn cid case included of- Just _ -> return False- _ -> case decodeStatelessResetToken cryptPacket of- Nothing -> return False- Just token -> isStatelessRestTokenValid conn cid token+ Just _ -> return False+ _ -> case decodeStatelessResetToken cryptPacket of+ Nothing -> return False+ Just token -> isStatelessRestTokenValid conn cid token -- Return value indicates duplication. putRxCrypto :: Connection -> EncryptionLevel -> RxStreamData -> IO Bool putRxCrypto conn lvl rx = do mstrm <- getCryptoStream conn lvl case mstrm of- Nothing -> return False- Just strm -> do- let put = putCrypto conn . InpHandshake lvl- putFin = return ()- tryReassemble strm rx put putFin+ Nothing -> return False+ Just strm -> do+ let put = putCrypto conn . InpHandshake lvl+ putFin = return ()+ tryReassemble strm rx put putFin killHandshaker :: Connection -> EncryptionLevel -> IO () killHandshaker conn lvl = putCrypto conn $ InpHandshake lvl ""
Network/QUIC/Recovery.hs view
@@ -1,51 +1,51 @@ module Network.QUIC.Recovery (- -- Interface- checkWindowOpenSTM- , takePingSTM- , speedup- , resender- -- LossRecovery.hs- , onPacketSent- , onPacketReceived- , onAckReceived- , onPacketNumberSpaceDiscarded- -- Metrics- , setInitialCongestionWindow- -- Misc- , getPreviousRTT1PPNs- , setPreviousRTT1PPNs- , getSpeedingUp- , getPacketNumberSpaceDiscarded- , getAndSetPacketNumberSpaceDiscarded- , setMaxAckDaley- -- PeerPacketNumbers- , getPeerPacketNumbers- , fromPeerPacketNumbers- , nullPeerPacketNumbers- -- Persistent- , findDuration- , getPTO- -- Release- , releaseByRetry- , releaseOldest- -- Timer- , beforeAntiAmp- , ldccTimer- -- Types- , SentPacket- , spPlainPacket- , spTimeSent- , spSentBytes- , spEncryptionLevel- , spPacketNumber- , spPeerPacketNumbers- , spAckEliciting- , mkSentPacket- , fixSentPacket- , LDCC- , newLDCC- , qlogSent- ) where+ -- Interface+ checkWindowOpenSTM,+ takePingSTM,+ speedup,+ resender,+ -- LossRecovery.hs+ onPacketSent,+ onPacketReceived,+ onAckReceived,+ onPacketNumberSpaceDiscarded,+ -- Metrics+ setInitialCongestionWindow,+ -- Misc+ getPreviousRTT1PPNs,+ setPreviousRTT1PPNs,+ getSpeedingUp,+ getPacketNumberSpaceDiscarded,+ getAndSetPacketNumberSpaceDiscarded,+ setMaxAckDaley,+ -- PeerPacketNumbers+ getPeerPacketNumbers,+ fromPeerPacketNumbers,+ nullPeerPacketNumbers,+ -- Persistent+ findDuration,+ getPTO,+ -- Release+ releaseByRetry,+ releaseOldest,+ -- Timer+ beforeAntiAmp,+ ldccTimer,+ -- Types+ SentPacket,+ spPlainPacket,+ spTimeSent,+ spSentBytes,+ spEncryptionLevel,+ spPacketNumber,+ spPeerPacketNumbers,+ spAckEliciting,+ mkSentPacket,+ fixSentPacket,+ LDCC,+ newLDCC,+ qlogSent,+) where import Network.QUIC.Recovery.Interface import Network.QUIC.Recovery.LossRecovery
Network/QUIC/Recovery/Constants.hs view
@@ -15,7 +15,6 @@ -- | Maximum reordering in time before time threshold loss detection -- considers a packet lost. Specified as an RTT multiplier.- kTimeThreshold :: Microseconds -> Microseconds kTimeThreshold x = x + (x !>>. 3) -- 9/8 @@ -26,7 +25,7 @@ -- | Default limit on the initial bytes in flight. kInitialWindow :: Int -> Int---kInitialWindow pktSiz = min 14720 (10 * pktSiz)+-- kInitialWindow pktSiz = min 14720 (10 * pktSiz) kInitialWindow pktSiz = pktSiz !<<. 2 -- !<<. 1 is not good enough -- | Minimum congestion window in bytes.
Network/QUIC/Recovery/Detect.hs view
@@ -2,12 +2,12 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Recovery.Detect (- releaseByPredicate- , detectAndRemoveLostPackets- , removePacketNumbers- ) where+ releaseByPredicate,+ detectAndRemoveLostPackets,+ removePacketNumbers,+) where -import Data.Sequence (Seq, ViewL(..))+import Data.Sequence (Seq, ViewL (..)) import qualified Data.Sequence as Seq import Network.QUIC.Imports@@ -17,11 +17,12 @@ import Network.QUIC.Recovery.Types import Network.QUIC.Types -releaseByPredicate :: LDCC -> EncryptionLevel -> (SentPacket -> Bool) -> IO (Seq SentPacket)+releaseByPredicate+ :: LDCC -> EncryptionLevel -> (SentPacket -> Bool) -> IO (Seq SentPacket) releaseByPredicate ldcc@LDCC{..} lvl predicate = do packets <- atomicModifyIORef' (sentPackets ! lvl) $ \(SentPackets db) ->- let (pkts, db') = Seq.partition predicate db- in (SentPackets db', pkts)+ let (pkts, db') = Seq.partition predicate db+ in (SentPackets db', pkts) removePacketNumbers ldcc lvl packets return packets @@ -29,48 +30,57 @@ detectAndRemoveLostPackets ldcc@LDCC{..} lvl = do lae <- timeOfLastAckElicitingPacket <$> readIORef (lossDetection ! lvl) when (lae == timeMicrosecond0) $- qlogDebug ldcc $ Debug "detectAndRemoveLostPackets: timeOfLastAckElicitingPacket: 0"- atomicModifyIORef'' (lossDetection ! lvl) $ \ld -> ld {- lossTime = Nothing- }+ qlogDebug ldcc $+ Debug "detectAndRemoveLostPackets: timeOfLastAckElicitingPacket: 0"+ atomicModifyIORef'' (lossDetection ! lvl) $ \ld ->+ ld+ { lossTime = Nothing+ } RTT{..} <- readIORef recoveryRTT LossDetection{..} <- readIORef (lossDetection ! lvl) when (largestAckedPacket == -1) $- qlogDebug ldcc $ Debug "detectAndRemoveLostPackets: largestAckedPacket: -1"+ qlogDebug ldcc $+ Debug "detectAndRemoveLostPackets: largestAckedPacket: -1" -- Sec 6.1.2. Time Threshold -- max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity) let lossDelay0 = kTimeThreshold $ max latestRTT smoothedRTT let lossDelay = max lossDelay0 kGranularity tm <- getPastTimeMicrosecond lossDelay- let predicate ent = (spPacketNumber ent <= largestAckedPacket - kPacketThreshold)- || (spPacketNumber ent <= largestAckedPacket && spTimeSent ent <= tm)+ let predicate ent =+ (spPacketNumber ent <= largestAckedPacket - kPacketThreshold)+ || (spPacketNumber ent <= largestAckedPacket && spTimeSent ent <= tm) lostPackets <- releaseByPredicate ldcc lvl predicate mx <- findOldest ldcc lvl (\x -> spPacketNumber x <= largestAckedPacket) case mx of- -- No gap packet. PTO turn.- Nothing -> return ()- -- There are gap packets which are not declared lost.- -- Set lossTime to next.- Just x -> do- let next = spTimeSent x `addMicroseconds` lossDelay- atomicModifyIORef'' (lossDetection ! lvl) $ \ld -> ld {- lossTime = Just next- }+ -- No gap packet. PTO turn.+ Nothing -> return ()+ -- There are gap packets which are not declared lost.+ -- Set lossTime to next.+ Just x -> do+ let next = spTimeSent x `addMicroseconds` lossDelay+ atomicModifyIORef'' (lossDetection ! lvl) $ \ld ->+ ld+ { lossTime = Just next+ } unless (Seq.null lostPackets) $ qlogDebug ldcc $ Debug "loss detected" return lostPackets -findOldest :: LDCC -> EncryptionLevel -> (SentPacket -> Bool)- -> IO (Maybe SentPacket)+findOldest+ :: LDCC+ -> EncryptionLevel+ -> (SentPacket -> Bool)+ -> IO (Maybe SentPacket) findOldest LDCC{..} lvl p = oldest <$> readIORef (sentPackets ! lvl) where oldest (SentPackets db) = case Seq.viewl $ Seq.filter p db of- EmptyL -> Nothing- x :< _ -> Just x+ EmptyL -> Nothing+ x :< _ -> Just x -removePacketNumbers :: Foldable t => LDCC -> EncryptionLevel -> t SentPacket -> IO ()+removePacketNumbers+ :: Foldable t => LDCC -> EncryptionLevel -> t SentPacket -> IO () removePacketNumbers ldcc lvl packets = mapM_ reduce packets where reduce x = reducePeerPacketNumbers ldcc lvl ppns
Network/QUIC/Recovery/Interface.hs view
@@ -2,11 +2,11 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Recovery.Interface (- checkWindowOpenSTM- , takePingSTM- , speedup- , resender- ) where+ checkWindowOpenSTM,+ takePingSTM,+ speedup,+ resender,+) where import qualified Data.Sequence as Seq import System.Log.FastLogger (LogStr)@@ -38,7 +38,7 @@ setSpeedingUp ldcc qlogDebug ldcc $ Debug desc packets <- atomicModifyIORef' (sentPackets ! lvl) $- \(SentPackets db) -> (emptySentPackets, db)+ \(SentPackets db) -> (emptySentPackets, db) -- don't clear PeerPacketNumbers. unless (null packets) $ do onPacketsLost ldcc packets
Network/QUIC/Recovery/LossRecovery.hs view
@@ -2,13 +2,13 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Recovery.LossRecovery (- onPacketSent- , onPacketReceived- , onAckReceived- , onPacketNumberSpaceDiscarded- ) where+ onPacketSent,+ onPacketReceived,+ onAckReceived,+ onPacketNumberSpaceDiscarded,+) where -import Data.Sequence (Seq, (|>), ViewR(..))+import Data.Sequence (Seq, ViewR (..), (|>)) import qualified Data.Sequence as Seq import UnliftIO.STM @@ -31,25 +31,29 @@ onPacketSent :: LDCC -> SentPacket -> IO () onPacketSent ldcc@LDCC{..} sentPacket = do let lvl0 = spEncryptionLevel sentPacket- let lvl | lvl0 == RTT0Level = RTT1Level- | otherwise = lvl0+ let lvl+ | lvl0 == RTT0Level = RTT1Level+ | otherwise = lvl0 discarded <- getPacketNumberSpaceDiscarded ldcc lvl unless discarded $ do onPacketSentCC ldcc sentPacket when (spAckEliciting sentPacket) $- atomicModifyIORef'' (lossDetection ! lvl) $ \ld -> ld {- timeOfLastAckElicitingPacket = spTimeSent sentPacket- }+ atomicModifyIORef'' (lossDetection ! lvl) $ \ld ->+ ld+ { timeOfLastAckElicitingPacket = spTimeSent sentPacket+ } atomicModifyIORef'' (sentPackets ! lvl) $ \(SentPackets db) -> SentPackets (db |> sentPacket) setLossDetectionTimer ldcc lvl onPacketSentCC :: LDCC -> SentPacket -> IO () onPacketSentCC ldcc@LDCC{..} sentPacket = metricsUpdated ldcc $- atomically $ modifyTVar' recoveryCC $ \cc -> cc {- bytesInFlight = bytesInFlight cc + sentBytes- , numOfAckEliciting = numOfAckEliciting cc + countAckEli sentPacket- }+ atomically $+ modifyTVar' recoveryCC $ \cc ->+ cc+ { bytesInFlight = bytesInFlight cc + sentBytes+ , numOfAckEliciting = numOfAckEliciting cc + countAckEli sentPacket+ } where sentBytes = spSentBytes sentPacket @@ -68,64 +72,72 @@ when changed $ do let predicate = fromAckInfoToPred ackInfo . spPacketNumber releaseLostCandidates ldcc lvl predicate >>= updateCConAck- releaseByPredicate ldcc lvl predicate >>= detectLossUpdateCC+ releaseByPredicate ldcc lvl predicate >>= detectLossUpdateCC where update ld@LossDetection{..} = (ld', changed) where- ld' = ld { largestAckedPacket = max largestAckedPacket largestAcked- , previousAckInfo = ackInfo- }+ ld' =+ ld+ { largestAckedPacket = max largestAckedPacket largestAcked+ , previousAckInfo = ackInfo+ } changed = previousAckInfo /= ackInfo detectLossUpdateCC newlyAckedPackets = case Seq.viewr newlyAckedPackets of- EmptyR -> return ()- _ :> lastPkt -> do- -- If the largest acknowledged is newly acked and- -- at least one ack-eliciting was newly acked, update the RTT.- when (spPacketNumber lastPkt == largestAcked- && any spAckEliciting newlyAckedPackets) $ do- rtt <- getElapsedTimeMicrosecond $ spTimeSent lastPkt- let latestRtt = max rtt kGranularity- updateRTT ldcc lvl latestRtt ackDelay+ EmptyR -> return ()+ _ :> lastPkt -> do+ -- If the largest acknowledged is newly acked and+ -- at least one ack-eliciting was newly acked, update the RTT.+ when+ ( spPacketNumber lastPkt == largestAcked+ && any spAckEliciting newlyAckedPackets+ )+ $ do+ rtt <- getElapsedTimeMicrosecond $ spTimeSent lastPkt+ let latestRtt = max rtt kGranularity+ updateRTT ldcc lvl latestRtt ackDelay - {- fimxe- -- Process ECN information if present.- if (ACK frame contains ECN information):- ProcessECN(ack, lvl)- -}+ {- fimxe+ -- Process ECN information if present.+ if (ACK frame contains ECN information):+ ProcessECN(ack, lvl)+ -} - lostPackets <- detectAndRemoveLostPackets ldcc lvl- unless (null lostPackets) $ do- mode <- ccMode <$> readTVarIO recoveryCC- if lvl == RTT1Level && mode /= SlowStart then- mergeLostCandidates ldcc lostPackets- else do- -- just in case- lostPackets' <- mergeLostCandidatesAndClear ldcc lostPackets- onPacketsLost ldcc lostPackets'- retransmit ldcc lostPackets'- -- setLossDetectionTimer in updateCConAck- updateCConAck newlyAckedPackets+ lostPackets <- detectAndRemoveLostPackets ldcc lvl+ unless (null lostPackets) $ do+ mode <- ccMode <$> readTVarIO recoveryCC+ if lvl == RTT1Level && mode /= SlowStart+ then mergeLostCandidates ldcc lostPackets+ else do+ -- just in case+ lostPackets' <- mergeLostCandidatesAndClear ldcc lostPackets+ onPacketsLost ldcc lostPackets'+ retransmit ldcc lostPackets'+ -- setLossDetectionTimer in updateCConAck+ updateCConAck newlyAckedPackets updateCConAck newlyAckedPackets- | newlyAckedPackets == Seq.empty = return ()- | otherwise = do- onPacketsAcked ldcc newlyAckedPackets+ | newlyAckedPackets == Seq.empty = return ()+ | otherwise = do+ onPacketsAcked ldcc newlyAckedPackets - -- Sec 6.2.1. Computing PTO- -- "The PTO backoff factor is reset when an acknowledgement is- -- received, except in the following case. A server might- -- take longer to respond to packets during the handshake- -- than otherwise. To protect such a server from repeated- -- client probes, the PTO backoff is not reset at a client- -- that is not yet certain that the server has finished- -- validating the client's address."- validated <- peerCompletedAddressValidation ldcc- when validated $ metricsUpdated ldcc $- atomicModifyIORef'' recoveryRTT $ \rtt -> rtt { ptoCount = 0 }+ -- Sec 6.2.1. Computing PTO+ -- "The PTO backoff factor is reset when an acknowledgement is+ -- received, except in the following case. A server might+ -- take longer to respond to packets during the handshake+ -- than otherwise. To protect such a server from repeated+ -- client probes, the PTO backoff is not reset at a client+ -- that is not yet certain that the server has finished+ -- validating the client's address."+ validated <- peerCompletedAddressValidation ldcc+ when validated $+ metricsUpdated ldcc $+ atomicModifyIORef'' recoveryRTT $+ \rtt -> rtt{ptoCount = 0} - setLossDetectionTimer ldcc lvl+ setLossDetectionTimer ldcc lvl -releaseLostCandidates :: LDCC -> EncryptionLevel -> (SentPacket -> Bool) -> IO (Seq SentPacket)+releaseLostCandidates+ :: LDCC -> EncryptionLevel -> (SentPacket -> Bool) -> IO (Seq SentPacket) releaseLostCandidates ldcc@LDCC{..} lvl predicate = do packets <- atomically $ do SentPackets db <- readTVar lostCandidates@@ -142,46 +154,51 @@ atomically $ modifyTVar' recoveryCC $ modify maxPktSiz newcc <- readTVarIO recoveryCC when (ccMode oldcc /= ccMode newcc) $- qlogContestionStateUpdated ldcc $ ccMode newcc+ qlogContestionStateUpdated ldcc $+ ccMode newcc where- modify maxPktSiz cc@CC{..} = cc {- bytesInFlight = bytesInFlight'- , congestionWindow = congestionWindow'- , bytesAcked = bytesAcked'- , ccMode = ccMode'- , numOfAckEliciting = numOfAckEliciting'- }+ modify maxPktSiz cc@CC{..} =+ cc+ { bytesInFlight = bytesInFlight'+ , congestionWindow = congestionWindow'+ , bytesAcked = bytesAcked'+ , ccMode = ccMode'+ , numOfAckEliciting = numOfAckEliciting'+ } where- (bytesInFlight',congestionWindow',bytesAcked',ccMode',numOfAckEliciting') =- foldl' (.+) (bytesInFlight,congestionWindow,bytesAcked,ccMode,numOfAckEliciting) ackedPackets- (bytes,cwin,acked,_,cnt) .+ sp@SentPacket{..} = (bytes',cwin',acked',mode',cnt')+ (bytesInFlight', congestionWindow', bytesAcked', ccMode', numOfAckEliciting') =+ foldl'+ (.+)+ (bytesInFlight, congestionWindow, bytesAcked, ccMode, numOfAckEliciting)+ ackedPackets+ (bytes, cwin, acked, _, cnt) .+ sp@SentPacket{..} = (bytes', cwin', acked', mode', cnt') where isRecovery = inCongestionRecovery spTimeSent congestionRecoveryStartTime bytes' = bytes - spSentBytes ackedA = acked + spSentBytes cnt' = cnt - countAckEli sp- (cwin',acked',mode')- -- Do not increase congestion window in recovery period.- | isRecovery = (cwin, acked, Recovery)- -- fixme: Do not increase congestion_window if application- -- limited or flow control limited.- --- -- Slow start.- | cwin < ssthresh = (cwin + spSentBytes, acked, SlowStart)- -- Congestion avoidance.- -- In this implementation, maxPktSiz == spSentBytes.- -- spSentBytes is large enough, so we don't care- -- the roundup issue of `div`.- | ackedA >= cwin = (cwin + maxPktSiz, ackedA - cwin, Avoidance)- | otherwise = (cwin, ackedA, Avoidance)+ (cwin', acked', mode')+ -- Do not increase congestion window in recovery period.+ | isRecovery = (cwin, acked, Recovery)+ -- fixme: Do not increase congestion_window if application+ -- limited or flow control limited.+ --+ -- Slow start.+ | cwin < ssthresh = (cwin + spSentBytes, acked, SlowStart)+ -- Congestion avoidance.+ -- In this implementation, maxPktSiz == spSentBytes.+ -- spSentBytes is large enough, so we don't care+ -- the roundup issue of `div`.+ | ackedA >= cwin = (cwin + maxPktSiz, ackedA - cwin, Avoidance)+ | otherwise = (cwin, ackedA, Avoidance) ---------------------------------------------------------------- onPacketNumberSpaceDiscarded :: LDCC -> EncryptionLevel -> IO () onPacketNumberSpaceDiscarded ldcc lvl = do- let (lvl',label) = case lvl of- InitialLevel -> (HandshakeLevel,"initial")- _ -> (RTT1Level, "handshake")+ let (lvl', label) = case lvl of+ InitialLevel -> (HandshakeLevel, "initial")+ _ -> (RTT1Level, "handshake") qlogDebug ldcc $ Debug (label <> " discarded") void $ discard ldcc lvl setLossDetectionTimer ldcc lvl'
Network/QUIC/Recovery/Metrics.hs view
@@ -1,13 +1,13 @@+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE NamedFieldPuns #-} module Network.QUIC.Recovery.Metrics (- updateRTT- , updateCC- , metricsUpdated- , setInitialCongestionWindow- ) where+ updateRTT,+ updateCC,+ metricsUpdated,+ setInitialCongestionWindow,+) where import Data.Sequence (Seq) import UnliftIO.STM@@ -35,19 +35,26 @@ -- -- smoothed_rtt = rtt_sample -- rttvar = rtt_sample / 2- update rtt@RTT{..} | latestRTT == Microseconds 0 = (rtt {- latestRTT = latestRTT0- , minRTT = latestRTT0- , smoothedRTT = latestRTT0- , rttvar = latestRTT0 `unsafeShiftR` 1- }, True)+ update rtt@RTT{..}+ | latestRTT == Microseconds 0 =+ ( rtt+ { latestRTT = latestRTT0+ , minRTT = latestRTT0+ , smoothedRTT = latestRTT0+ , rttvar = latestRTT0 `unsafeShiftR` 1+ }+ , True+ ) -- Others:- update rtt@RTT{..} = (rtt {- latestRTT = latestRTT0- , minRTT = minRTT'- , smoothedRTT = smoothedRTT'- , rttvar = rttvar'- }, False)+ update rtt@RTT{..} =+ ( rtt+ { latestRTT = latestRTT0+ , minRTT = minRTT'+ , smoothedRTT = smoothedRTT'+ , rttvar = rttvar'+ }+ , False+ ) where -- minRTT ignores ack delay. minRTT' = min minRTT latestRTT0@@ -59,15 +66,19 @@ -- if (latest_rtt >= min_rtt + ack_delay): -- adjusted_rtt = latest_rtt - ack_delay adjustedRTT- | latestRTT0 >= minRTT + ackDelay = latestRTT0 - ackDelay- | otherwise = latestRTT0+ | latestRTT0 >= minRTT + ackDelay = latestRTT0 - ackDelay+ | otherwise = latestRTT0 -- rttvar_sample = abs(smoothed_rtt - adjusted_rtt) -- rttvar = 3/4 * rttvar + 1/4 * rttvar_sample- rttvar' = rttvar - (rttvar !>>. 2)+ rttvar' =+ rttvar+ - (rttvar !>>. 2) + (abs (smoothedRTT - adjustedRTT) !>>. 2) -- smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt- smoothedRTT' = smoothedRTT - (smoothedRTT !>>. 3)- + (adjustedRTT !>>. 3)+ smoothedRTT' =+ smoothedRTT+ - (smoothedRTT !>>. 3)+ + (adjustedRTT !>>. 3) updateCC :: LDCC -> Seq SentPacket -> Bool -> IO () updateCC ldcc@LDCC{..} lostPackets isRecovery = do@@ -78,27 +89,29 @@ metricsUpdated ldcc $ atomically $ modifyTVar' recoveryCC $ \cc@CC{..} -> let halfWindow = max minWindow $ kLossReductionFactor congestionWindow cwin- | persistent = minWindow- | otherwise = halfWindow- sst = halfWindow+ | persistent = minWindow+ | otherwise = halfWindow+ sst = halfWindow mode- | cwin < sst = SlowStart -- persistent- | otherwise = Recovery- in cc {- congestionRecoveryStartTime = Just now- , congestionWindow = cwin- , ssthresh = sst- , ccMode = mode- , bytesAcked = 0- }+ | cwin < sst = SlowStart -- persistent+ | otherwise = Recovery+ in cc+ { congestionRecoveryStartTime = Just now+ , congestionWindow = cwin+ , ssthresh = sst+ , ccMode = mode+ , bytesAcked = 0+ } CC{ccMode} <- readTVarIO recoveryCC qlogContestionStateUpdated ldcc ccMode setInitialCongestionWindow :: LDCC -> Int -> IO () setInitialCongestionWindow ldcc@LDCC{..} pktSiz = metricsUpdated ldcc $- atomically $ do modifyTVar' recoveryCC $ \cc -> cc {- congestionWindow = kInitialWindow pktSiz- }+ atomically $ do+ modifyTVar' recoveryCC $ \cc ->+ cc+ { congestionWindow = kInitialWindow pktSiz+ } ---------------------------------------------------------------- @@ -109,21 +122,22 @@ body rtt1 <- readIORef recoveryRTT cc1 <- readTVarIO recoveryCC- let ~diff = catMaybes [- time "min_rtt" (minRTT rtt0) (minRTT rtt1)- , time "smoothed_rtt" (smoothedRTT rtt0) (smoothedRTT rtt1)- , time "latest_rtt" (latestRTT rtt0) (latestRTT rtt1)- , time "rtt_variance" (rttvar rtt0) (rttvar rtt1)- , numb "pto_count" (ptoCount rtt0) (ptoCount rtt1)- , numb "bytes_in_flight" (bytesInFlight cc0) (bytesInFlight cc1)- , numb "congestion_window" (congestionWindow cc0) (congestionWindow cc1)- , numb "ssthresh" (ssthresh cc0) (ssthresh cc1)- ]+ let ~diff =+ catMaybes+ [ time "min_rtt" (minRTT rtt0) (minRTT rtt1)+ , time "smoothed_rtt" (smoothedRTT rtt0) (smoothedRTT rtt1)+ , time "latest_rtt" (latestRTT rtt0) (latestRTT rtt1)+ , time "rtt_variance" (rttvar rtt0) (rttvar rtt1)+ , numb "pto_count" (ptoCount rtt0) (ptoCount rtt1)+ , numb "bytes_in_flight" (bytesInFlight cc0) (bytesInFlight cc1)+ , numb "congestion_window" (congestionWindow cc0) (congestionWindow cc1)+ , numb "ssthresh" (ssthresh cc0) (ssthresh cc1)+ ] unless (null diff) $ qlogMetricsUpdated ldcc $ MetricsDiff diff where time tag (Microseconds v0) (Microseconds v1)- | v0 == v1 = Nothing- | otherwise = Just (tag,v1)+ | v0 == v1 = Nothing+ | otherwise = Just (tag, v1) numb tag v0 v1- | v0 == v1 = Nothing- | otherwise = Just (tag,v1)+ | v0 == v1 = Nothing+ | otherwise = Just (tag, v1)
Network/QUIC/Recovery/Misc.hs view
@@ -2,14 +2,14 @@ {-# LANGUAGE TupleSections #-} module Network.QUIC.Recovery.Misc (- getPktNumPersistent- , setPktNumPersistent- , setSpeedingUp- , getSpeedingUp- , getPacketNumberSpaceDiscarded- , getAndSetPacketNumberSpaceDiscarded- , setMaxAckDaley- ) where+ getPktNumPersistent,+ setPktNumPersistent,+ setSpeedingUp,+ getSpeedingUp,+ getPacketNumberSpaceDiscarded,+ getAndSetPacketNumberSpaceDiscarded,+ setMaxAckDaley,+) where import Data.IORef @@ -50,4 +50,4 @@ setMaxAckDaley :: LDCC -> Microseconds -> IO () setMaxAckDaley LDCC{..} delay0 =- atomicModifyIORef'' recoveryRTT $ \rtt -> rtt { maxAckDelay1RTT = delay0 }+ atomicModifyIORef'' recoveryRTT $ \rtt -> rtt{maxAckDelay1RTT = delay0}
Network/QUIC/Recovery/PeerPacketNumbers.hs view
@@ -1,16 +1,16 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Recovery.PeerPacketNumbers (- getPeerPacketNumbers- , addPeerPacketNumbers- , delPeerPacketNumbers- , clearPeerPacketNumbers- , reducePeerPacketNumbers- , setPreviousRTT1PPNs- , getPreviousRTT1PPNs- , nullPeerPacketNumbers- , fromPeerPacketNumbers- ) where+ getPeerPacketNumbers,+ addPeerPacketNumbers,+ delPeerPacketNumbers,+ clearPeerPacketNumbers,+ reducePeerPacketNumbers,+ setPreviousRTT1PPNs,+ getPreviousRTT1PPNs,+ nullPeerPacketNumbers,+ fromPeerPacketNumbers,+) where import qualified Data.IntSet as IntSet
Network/QUIC/Recovery/Persistent.hs view
@@ -2,15 +2,15 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Recovery.Persistent (- getMaxAckDelay- , calcPTO- , backOff- , inPersistentCongestion- , findDuration -- for testing- , getPTO- ) where+ getMaxAckDelay,+ calcPTO,+ backOff,+ inPersistentCongestion,+ findDuration, -- for testing+ getPTO,+) where -import Data.Sequence (Seq, ViewL(..))+import Data.Sequence (Seq, ViewL (..)) import qualified Data.Sequence as Seq import Data.UnixTime @@ -23,8 +23,8 @@ getMaxAckDelay :: Maybe EncryptionLevel -> Microseconds -> Microseconds getMaxAckDelay Nothing n = n getMaxAckDelay (Just lvl) n- | lvl `elem` [InitialLevel,HandshakeLevel] = 0- | otherwise = n+ | lvl `elem` [InitialLevel, HandshakeLevel] = 0+ | otherwise = n -- Sec 6.2.1. Computing PTO -- PTO = smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay@@ -42,48 +42,46 @@ pn <- getPktNumPersistent ldcc let mduration = findDuration lostPackets pn case mduration of- Nothing -> return False- Just duration -> do- rtt <- readIORef recoveryRTT- let pto = calcPTO rtt Nothing- Microseconds congestionPeriod = kPersistentCongestionThreshold pto- threshold = microSecondsToUnixDiffTime congestionPeriod- return (duration > threshold)+ Nothing -> return False+ Just duration -> do+ rtt <- readIORef recoveryRTT+ let pto = calcPTO rtt Nothing+ Microseconds congestionPeriod = kPersistentCongestionThreshold pto+ threshold = microSecondsToUnixDiffTime congestionPeriod+ return (duration > threshold) findDuration :: Seq SentPacket -> PacketNumber -> Maybe UnixDiffTime findDuration pkts0 pn = leftEdge pkts0 Nothing where leftEdge pkts mdiff = case Seq.viewl pkts' of- EmptyL -> mdiff+ EmptyL -> mdiff l :< pkts'' -> case rightEdge (spPacketNumber l) pkts'' Nothing of- (Nothing, pkts''') -> leftEdge pkts''' mdiff- (Just r, pkts''') ->- let diff' = spTimeSent r `diffUnixTime` spTimeSent l- in case mdiff of- Nothing -> leftEdge pkts''' $ Just diff'- Just diff- | diff' > diff -> leftEdge pkts''' $ Just diff'- | otherwise -> leftEdge pkts''' $ Just diff+ (Nothing, pkts''') -> leftEdge pkts''' mdiff+ (Just r, pkts''') ->+ let diff' = spTimeSent r `diffUnixTime` spTimeSent l+ in case mdiff of+ Nothing -> leftEdge pkts''' $ Just diff'+ Just diff+ | diff' > diff -> leftEdge pkts''' $ Just diff'+ | otherwise -> leftEdge pkts''' $ Just diff where (_, pkts') = Seq.breakl (\x -> spAckEliciting x && spPacketNumber x >= pn) pkts rightEdge n pkts Nothing = case Seq.viewl pkts of EmptyL -> (Nothing, Seq.empty) r :< pkts'- | spPacketNumber r == n + 1 ->- if spAckEliciting r then- rightEdge (n + 1) pkts' $ Just r- else- rightEdge (n + 1) pkts' Nothing- | otherwise -> (Nothing, pkts)+ | spPacketNumber r == n + 1 ->+ if spAckEliciting r+ then rightEdge (n + 1) pkts' $ Just r+ else rightEdge (n + 1) pkts' Nothing+ | otherwise -> (Nothing, pkts) rightEdge n pkts mr0 = case Seq.viewl pkts of EmptyL -> (mr0, Seq.empty) r :< pkts'- | spPacketNumber r == n + 1 ->- if spAckEliciting r then- rightEdge (n + 1) pkts' $ Just r- else- rightEdge (n + 1) pkts' mr0- | otherwise -> (mr0, pkts)+ | spPacketNumber r == n + 1 ->+ if spAckEliciting r+ then rightEdge (n + 1) pkts' $ Just r+ else rightEdge (n + 1) pkts' mr0+ | otherwise -> (mr0, pkts) getPTO :: LDCC -> IO Microseconds getPTO LDCC{..} = do
Network/QUIC/Recovery/Release.hs view
@@ -2,13 +2,13 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Recovery.Release (- releaseByRetry- , releaseOldest- , discard- , onPacketsLost- ) where+ releaseByRetry,+ releaseOldest,+ discard,+ onPacketsLost,+) where -import Data.Sequence (Seq, (><), ViewL(..), ViewR(..))+import Data.Sequence (Seq, ViewL (..), ViewR (..), (><)) import qualified Data.Sequence as Seq import UnliftIO.STM @@ -27,7 +27,8 @@ decreaseCC ldcc packets writeIORef (lossDetection ! lvl) initialLossDetection metricsUpdated ldcc $- atomicModifyIORef'' recoveryRTT $ \rtt -> rtt { ptoCount = 0 }+ atomicModifyIORef'' recoveryRTT $+ \rtt -> rtt{ptoCount = 0} return packets releaseByClear :: LDCC -> EncryptionLevel -> IO (Seq SentPacket)@@ -48,16 +49,17 @@ releaseOldest ldcc@LDCC{..} lvl = do mr <- atomicModifyIORef' (sentPackets ! lvl) oldest case mr of- Nothing -> return ()- Just spkt -> do- delPeerPacketNumbers ldcc lvl $ spPacketNumber spkt- decreaseCC ldcc [spkt]+ Nothing -> return ()+ Just spkt -> do+ delPeerPacketNumbers ldcc lvl $ spPacketNumber spkt+ decreaseCC ldcc [spkt] return mr where oldest (SentPackets db) = case Seq.viewl db2 of- x :< db2' -> let db' = db1 >< db2'- in (SentPackets db', Just x)- _ -> (SentPackets db, Nothing)+ x :< db2' ->+ let db' = db1 >< db2'+ in (SentPackets db', Just x)+ _ -> (SentPackets db, Nothing) where (db1, db2) = Seq.breakl spAckEliciting db @@ -65,12 +67,14 @@ onPacketsLost :: LDCC -> Seq SentPacket -> IO () onPacketsLost ldcc@LDCC{..} lostPackets = case Seq.viewr lostPackets of- EmptyR -> return ()- _ :> lastPkt -> do- decreaseCC ldcc lostPackets- isRecovery <- inCongestionRecovery (spTimeSent lastPkt) . congestionRecoveryStartTime <$> readTVarIO recoveryCC- onCongestionEvent ldcc lostPackets isRecovery- mapM_ (qlogPacketLost ldcc . LostPacket) lostPackets+ EmptyR -> return ()+ _ :> lastPkt -> do+ decreaseCC ldcc lostPackets+ isRecovery <-+ inCongestionRecovery (spTimeSent lastPkt) . congestionRecoveryStartTime+ <$> readTVarIO recoveryCC+ onCongestionEvent ldcc lostPackets isRecovery+ mapM_ (qlogPacketLost ldcc . LostPacket) lostPackets where onCongestionEvent = updateCC @@ -81,9 +85,9 @@ let sentBytes = sum' (spSentBytes <$> packets) num = sum' (countAckEli <$> packets) metricsUpdated ldcc $- atomically $ modifyTVar' recoveryCC $ \cc ->- cc {- bytesInFlight = bytesInFlight cc - sentBytes-- , numOfAckEliciting = numOfAckEliciting cc - num- }+ atomically $+ modifyTVar' recoveryCC $ \cc ->+ cc+ { bytesInFlight = bytesInFlight cc - sentBytes+ , numOfAckEliciting = numOfAckEliciting cc - num+ }
Network/QUIC/Recovery/Timer.hs view
@@ -3,12 +3,12 @@ {-# LANGUAGE TupleSections #-} module Network.QUIC.Recovery.Timer (- getLossTimeAndSpace- , getPtoTimeAndSpace- , setLossDetectionTimer- , beforeAntiAmp- , ldccTimer- ) where+ getLossTimeAndSpace,+ getPtoTimeAndSpace,+ setLossDetectionTimer,+ beforeAntiAmp,+ ldccTimer,+) where import qualified Data.Sequence as Seq import Network.QUIC.Event@@ -17,6 +17,7 @@ import Network.QUIC.Connector import Network.QUIC.Imports import Network.QUIC.Qlog+import Network.QUIC.Recovery.Constants import Network.QUIC.Recovery.Detect import Network.QUIC.Recovery.Metrics import Network.QUIC.Recovery.Misc@@ -24,7 +25,6 @@ import Network.QUIC.Recovery.Release import Network.QUIC.Recovery.Types import Network.QUIC.Recovery.Utils-import Network.QUIC.Recovery.Constants import Network.QUIC.Types ----------------------------------------------------------------@@ -34,20 +34,20 @@ SentPackets db <- readIORef (sentPackets ! lvl) return $ Seq.null db -getLossTimeAndSpace :: LDCC -> IO (Maybe (TimeMicrosecond,EncryptionLevel))+getLossTimeAndSpace :: LDCC -> IO (Maybe (TimeMicrosecond, EncryptionLevel)) getLossTimeAndSpace LDCC{..} = loop [InitialLevel, HandshakeLevel, RTT1Level] Nothing where- loop [] r = return r- loop (l:ls) r = do+ loop [] r = return r+ loop (l : ls) r = do mt <- lossTime <$> readIORef (lossDetection ! l) case mt of- Nothing -> loop ls r- Just t -> case r of- Nothing -> loop ls $ Just (t,l)- Just (t0,_)- | t < t0 -> loop ls $ Just (t,l)- | otherwise -> loop ls r+ Nothing -> loop ls r+ Just t -> case r of+ Nothing -> loop ls $ Just (t, l)+ Just (t0, _)+ | t < t0 -> loop ls $ Just (t, l)+ | otherwise -> loop ls r ---------------------------------------------------------------- @@ -55,39 +55,42 @@ getPtoTimeAndSpace ldcc@LDCC{..} = do -- Arm PTO from now when there are no inflight packets. CC{..} <- readTVarIO recoveryCC- if bytesInFlight <= 0 then do- validated <- peerCompletedAddressValidation ldcc- if validated then do- qlogDebug ldcc $ Debug "getPtoTimeAndSpace: validated"- return Nothing- else do- rtt <- readIORef recoveryRTT- lvl <- getEncryptionLevel ldcc- let pto = backOff (calcPTO rtt $ Just lvl) (ptoCount rtt)- ptoTime <- getFutureTimeMicrosecond pto- return $ Just (ptoTime, lvl)- else do- completed <- isConnectionEstablished ldcc- let lvls | completed = [InitialLevel, HandshakeLevel, RTT1Level]- | otherwise = [InitialLevel, HandshakeLevel]- loop lvls+ if bytesInFlight <= 0+ then do+ validated <- peerCompletedAddressValidation ldcc+ if validated+ then do+ qlogDebug ldcc $ Debug "getPtoTimeAndSpace: validated"+ return Nothing+ else do+ rtt <- readIORef recoveryRTT+ lvl <- getEncryptionLevel ldcc+ let pto = backOff (calcPTO rtt $ Just lvl) (ptoCount rtt)+ ptoTime <- getFutureTimeMicrosecond pto+ return $ Just (ptoTime, lvl)+ else do+ completed <- isConnectionEstablished ldcc+ let lvls+ | completed = [InitialLevel, HandshakeLevel, RTT1Level]+ | otherwise = [InitialLevel, HandshakeLevel]+ loop lvls where loop :: [EncryptionLevel] -> IO (Maybe (TimeMicrosecond, EncryptionLevel)) loop [] = return Nothing- loop (l:ls) = do+ loop (l : ls) = do notInFlight <- noInFlightPacket ldcc l- if notInFlight then- loop ls- else do- LossDetection{..} <- readIORef (lossDetection ! l)- if timeOfLastAckElicitingPacket == timeMicrosecond0 then- loop ls- else do- rtt <- readIORef recoveryRTT- let pto0 = backOff (calcPTO rtt $ Just l) (ptoCount rtt)- pto = max pto0 kGranularity- ptoTime = timeOfLastAckElicitingPacket `addMicroseconds` pto- return $ Just (ptoTime, l)+ if notInFlight+ then loop ls+ else do+ LossDetection{..} <- readIORef (lossDetection ! l)+ if timeOfLastAckElicitingPacket == timeMicrosecond0+ then loop ls+ else do+ rtt <- readIORef recoveryRTT+ let pto0 = backOff (calcPTO rtt $ Just l) (ptoCount rtt)+ pto = max pto0 kGranularity+ ptoTime = timeOfLastAckElicitingPacket `addMicroseconds` pto+ return $ Just (ptoTime, l) ---------------------------------------------------------------- @@ -105,10 +108,9 @@ updateLossDetectionTimer ldcc@LDCC{..} tmi = do mtmi <- readIORef timerInfo when (mtmi /= Just tmi) $ do- if timerLevel tmi == RTT1Level then- atomically $ writeTVar timerInfoQ $ Next tmi- else- updateLossDetectionTimer' ldcc tmi+ if timerLevel tmi == RTT1Level+ then atomically $ writeTVar timerInfoQ $ Next tmi+ else updateLossDetectionTimer' ldcc tmi ldccTimer :: LDCC -> IO () ldccTimer ldcc@LDCC{..} = forever $ do@@ -122,16 +124,17 @@ updateWithNext ldcc@LDCC{..} = do x <- readTVarIO timerInfoQ case x of- Empty -> return ()- Next tmi -> updateLossDetectionTimer' ldcc tmi+ Empty -> return ()+ Next tmi -> updateLossDetectionTimer' ldcc tmi updateLossDetectionTimer' :: LDCC -> TimerInfo -> IO () updateLossDetectionTimer' ldcc@LDCC{..} tmi = do atomically $ writeTVar timerInfoQ Empty let tim = timerTime tmi Microseconds us0 <- getTimeoutInMicrosecond tim- let us | us0 <= 0 = 10000 -- fixme- | otherwise = us0+ let us+ | us0 <= 0 = 10000 -- fixme+ | otherwise = us0 update us qlogLossTimerUpdated ldcc (tmi, Microseconds us) -- fixme tmi where@@ -148,30 +151,30 @@ setLossDetectionTimer ldcc@LDCC{..} lvl0 = do mtl <- getLossTimeAndSpace ldcc case mtl of- Just (earliestLossTime,lvl) -> do- when (lvl0 == lvl) $ do- -- Time threshold loss detection.- let tmi = TimerInfo earliestLossTime lvl LossTime- updateLossDetectionTimer ldcc tmi- Nothing -> do- -- See beforeAntiAmp- CC{..} <- readTVarIO recoveryCC- validated <- peerCompletedAddressValidation ldcc- if numOfAckEliciting <= 0 && validated then- -- There is nothing to detect lost, so no timer is- -- set. However, we only do this if the peer has- -- been validated, to prevent the server from being- -- blocked by the anti-amplification limit.- cancelLossDetectionTimer ldcc- else do- -- Determine which PN space to arm PTO for.- mx <- getPtoTimeAndSpace ldcc- case mx of- Nothing -> return ()- Just (ptoTime, lvl) -> do- when (lvl0 == lvl) $ do- let tmi = TimerInfo ptoTime lvl PTO- updateLossDetectionTimer ldcc tmi+ Just (earliestLossTime, lvl) -> do+ when (lvl0 == lvl) $ do+ -- Time threshold loss detection.+ let tmi = TimerInfo earliestLossTime lvl LossTime+ updateLossDetectionTimer ldcc tmi+ Nothing -> do+ -- See beforeAntiAmp+ CC{..} <- readTVarIO recoveryCC+ validated <- peerCompletedAddressValidation ldcc+ if numOfAckEliciting <= 0 && validated+ then -- There is nothing to detect lost, so no timer is+ -- set. However, we only do this if the peer has+ -- been validated, to prevent the server from being+ -- blocked by the anti-amplification limit.+ cancelLossDetectionTimer ldcc+ else do+ -- Determine which PN space to arm PTO for.+ mx <- getPtoTimeAndSpace ldcc+ case mx of+ Nothing -> return ()+ Just (ptoTime, lvl) -> do+ when (lvl0 == lvl) $ do+ let tmi = TimerInfo ptoTime lvl PTO+ updateLossDetectionTimer ldcc tmi beforeAntiAmp :: LDCC -> IO () beforeAntiAmp ldcc = cancelLossDetectionTimer ldcc@@ -187,42 +190,42 @@ when alive $ do mtmi <- readIORef timerInfo case mtmi of- Nothing -> return ()- Just tmi -> do- let lvl = timerLevel tmi- discarded <- getPacketNumberSpaceDiscarded ldcc lvl- if discarded then- updateWithNext ldcc- else- lossTimeOrPTO lvl tmi+ Nothing -> return ()+ Just tmi -> do+ let lvl = timerLevel tmi+ discarded <- getPacketNumberSpaceDiscarded ldcc lvl+ if discarded+ then updateWithNext ldcc+ else lossTimeOrPTO lvl tmi where lossTimeOrPTO lvl tmi = do qlogLossTimerExpired ldcc case timerType tmi of- LossTime -> do- -- Time threshold loss Detection- lostPackets <- detectAndRemoveLostPackets ldcc lvl- lostPackets' <- mergeLostCandidatesAndClear ldcc lostPackets- when (null lostPackets') $ qlogDebug ldcc $ Debug "onLossDetectionTimeout: null"- onPacketsLost ldcc lostPackets'- retransmit ldcc lostPackets'- setLossDetectionTimer ldcc lvl- PTO -> do- CC{..} <- readTVarIO recoveryCC- if bytesInFlight > 0 then do- -- PTO. Send new data if available, else retransmit old data.- -- If neither is available, send a single PING frame.- sendPing ldcc lvl- else do- -- Client sends an anti-deadlock packet: Initial is padded- -- to earn more anti-amplification credit,- -- a Handshake packet proves address ownership.- validated <- peerCompletedAddressValidation ldcc- when validated $ qlogDebug ldcc $ Debug "onLossDetectionTimeout: RTT1"- lvl' <- getEncryptionLevel ldcc -- fixme- sendPing ldcc lvl'+ LossTime -> do+ -- Time threshold loss Detection+ lostPackets <- detectAndRemoveLostPackets ldcc lvl+ lostPackets' <- mergeLostCandidatesAndClear ldcc lostPackets+ when (null lostPackets') $ qlogDebug ldcc $ Debug "onLossDetectionTimeout: null"+ onPacketsLost ldcc lostPackets'+ retransmit ldcc lostPackets'+ setLossDetectionTimer ldcc lvl+ PTO -> do+ CC{..} <- readTVarIO recoveryCC+ if bytesInFlight > 0+ then do+ -- PTO. Send new data if available, else retransmit old data.+ -- If neither is available, send a single PING frame.+ sendPing ldcc lvl+ else do+ -- Client sends an anti-deadlock packet: Initial is padded+ -- to earn more anti-amplification credit,+ -- a Handshake packet proves address ownership.+ validated <- peerCompletedAddressValidation ldcc+ when validated $ qlogDebug ldcc $ Debug "onLossDetectionTimeout: RTT1"+ lvl' <- getEncryptionLevel ldcc -- fixme+ sendPing ldcc lvl' - metricsUpdated ldcc $- atomicModifyIORef'' recoveryRTT $- \rtt -> rtt { ptoCount = ptoCount rtt + 1 }- setLossDetectionTimer ldcc lvl+ metricsUpdated ldcc $+ atomicModifyIORef'' recoveryRTT $+ \rtt -> rtt{ptoCount = ptoCount rtt + 1}+ setLossDetectionTimer ldcc lvl
Network/QUIC/Recovery/Types.hs view
@@ -3,37 +3,37 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Recovery.Types (- SentPacket(..)- , mkSentPacket- , fixSentPacket- , LostPacket(..)- , SentPackets(..)- , emptySentPackets- , RTT(..)- , initialRTT- , CCMode(..)- , CC(..)- , initialCC- , LossDetection(..)- , initialLossDetection- , MetricsDiff(..)- , TimerType(..)- , TimerInfo(..)- , TimerInfoQ(..)- , TimerCancelled- , TimerExpired- , makeSentPackets- , makeLossDetection- , LDCC(..)- , newLDCC- , qlogSent- , qlogMetricsUpdated- , qlogPacketLost- , qlogContestionStateUpdated- , qlogLossTimerUpdated- , qlogLossTimerCancelled- , qlogLossTimerExpired- ) where+ SentPacket (..),+ mkSentPacket,+ fixSentPacket,+ LostPacket (..),+ SentPackets (..),+ emptySentPackets,+ RTT (..),+ initialRTT,+ CCMode (..),+ CC (..),+ initialCC,+ LossDetection (..),+ initialLossDetection,+ MetricsDiff (..),+ TimerType (..),+ TimerInfo (..),+ TimerInfoQ (..),+ TimerCancelled,+ TimerExpired,+ makeSentPackets,+ makeLossDetection,+ LDCC (..),+ newLDCC,+ qlogSent,+ qlogMetricsUpdated,+ qlogPacketLost,+ qlogContestionStateUpdated,+ qlogLossTimerUpdated,+ qlogLossTimerCancelled,+ qlogLossTimerExpired,+) where import Data.IORef import Data.List (intersperse)@@ -50,178 +50,199 @@ ---------------------------------------------------------------- -data SentPacket = SentPacket {- spPlainPacket :: PlainPacket- , spTimeSent :: TimeMicrosecond- , spSentBytes :: Int- , spEncryptionLevel :: EncryptionLevel- , spPacketNumber :: PacketNumber- , spPeerPacketNumbers :: PeerPacketNumbers- , spAckEliciting :: Bool- } deriving (Eq, Show)+data SentPacket = SentPacket+ { spPlainPacket :: PlainPacket+ , spTimeSent :: TimeMicrosecond+ , spSentBytes :: Int+ , spEncryptionLevel :: EncryptionLevel+ , spPacketNumber :: PacketNumber+ , spPeerPacketNumbers :: PeerPacketNumbers+ , spAckEliciting :: Bool+ }+ deriving (Eq, Show) instance Ord SentPacket where x <= y = spPacketNumber x <= spPacketNumber y newtype LostPacket = LostPacket SentPacket -mkSentPacket :: PacketNumber -> EncryptionLevel -> PlainPacket -> PeerPacketNumbers -> Bool -> SentPacket-mkSentPacket mypn lvl ppkt ppns ackeli = SentPacket {- spPlainPacket = ppkt- , spTimeSent = timeMicrosecond0- , spSentBytes = 0- , spEncryptionLevel = lvl- , spPacketNumber = mypn- , spPeerPacketNumbers = ppns- , spAckEliciting = ackeli- }+mkSentPacket+ :: PacketNumber+ -> EncryptionLevel+ -> PlainPacket+ -> PeerPacketNumbers+ -> Bool+ -> SentPacket+mkSentPacket mypn lvl ppkt ppns ackeli =+ SentPacket+ { spPlainPacket = ppkt+ , spTimeSent = timeMicrosecond0+ , spSentBytes = 0+ , spEncryptionLevel = lvl+ , spPacketNumber = mypn+ , spPeerPacketNumbers = ppns+ , spAckEliciting = ackeli+ } fixSentPacket :: SentPacket -> Int -> Int -> SentPacket-fixSentPacket spkt bytes padLen = spkt {- spPlainPacket = if padLen /= 0 then addPadding padLen $ spPlainPacket spkt- else spPlainPacket spkt- , spSentBytes = bytes- }+fixSentPacket spkt bytes padLen =+ spkt+ { spPlainPacket =+ if padLen /= 0+ then addPadding padLen $ spPlainPacket spkt+ else spPlainPacket spkt+ , spSentBytes = bytes+ } addPadding :: Int -> PlainPacket -> PlainPacket addPadding n (PlainPacket hdr plain) = PlainPacket hdr plain' where- plain' = plain {- plainFrames = plainFrames plain ++ [Padding n]- }+ plain' =+ plain+ { plainFrames = plainFrames plain ++ [Padding n]+ } ---------------------------------------------------------------- -newtype SentPackets = SentPackets (Seq SentPacket) deriving Eq+newtype SentPackets = SentPackets (Seq SentPacket) deriving (Eq) emptySentPackets :: SentPackets emptySentPackets = SentPackets Seq.empty ---------------------------------------------------------------- -data RTT = RTT {- -- | The most recent RTT measurement made when receiving an ack for- -- a previously unacked packet.- latestRTT :: Microseconds- -- | The smoothed RTT of the connection.- , smoothedRTT :: Microseconds- -- | The RTT variation.- , rttvar :: Microseconds- -- | The minimum RTT seen in the connection, ignoring ack delay.- , minRTT :: Microseconds- -- | The maximum amount of time by which the receiver intends to- -- delay acknowledgments for packets in the ApplicationData packet- -- number space. The actual ack_delay in a received ACK frame may- -- be larger due to late timers, reordering, or lost ACK frames.- , maxAckDelay1RTT :: Microseconds- -- | The number of times a PTO has been sent without receiving- -- an ack.- , ptoCount :: Int- } deriving Show+data RTT = RTT+ { latestRTT :: Microseconds+ -- ^ The most recent RTT measurement made when receiving an ack for+ -- a previously unacked packet.+ , smoothedRTT :: Microseconds+ -- ^ The smoothed RTT of the connection.+ , rttvar :: Microseconds+ -- ^ The RTT variation.+ , minRTT :: Microseconds+ -- ^ The minimum RTT seen in the connection, ignoring ack delay.+ , maxAckDelay1RTT :: Microseconds+ -- ^ The maximum amount of time by which the receiver intends to+ -- delay acknowledgments for packets in the ApplicationData packet+ -- number space. The actual ack_delay in a received ACK frame may+ -- be larger due to late timers, reordering, or lost ACK frames.+ , ptoCount :: Int+ -- ^ The number of times a PTO has been sent without receiving+ -- an ack.+ }+ deriving (Show) -- | The RTT used before an RTT sample is taken. kInitialRTT :: Microseconds kInitialRTT = Microseconds 333000 initialRTT :: RTT-initialRTT = RTT {- latestRTT = Microseconds 0- , smoothedRTT = kInitialRTT- , rttvar = kInitialRTT !>>. 1- , minRTT = Microseconds 0- , maxAckDelay1RTT = Microseconds 0- , ptoCount = 0- }+initialRTT =+ RTT+ { latestRTT = Microseconds 0+ , smoothedRTT = kInitialRTT+ , rttvar = kInitialRTT !>>. 1+ , minRTT = Microseconds 0+ , maxAckDelay1RTT = Microseconds 0+ , ptoCount = 0+ } ---------------------------------------------------------------- -data CCMode = SlowStart- | Avoidance- | Recovery- deriving (Eq)+data CCMode+ = SlowStart+ | Avoidance+ | Recovery+ deriving (Eq) instance Show CCMode where show SlowStart = "slow_start" show Avoidance = "avoidance"- show Recovery = "recovery"+ show Recovery = "recovery" -data CC = CC {- -- | The sum of the size in bytes of all sent packets that contain- -- at least one ack-eliciting or PADDING frame, and have not been- -- acked or declared lost. The size does not include IP or UDP- -- overhead, but does include the QUIC header and AEAD overhead.- -- Packets only containing ACK frames do not count towards- -- bytes_in_flight to ensure congestion control does not impede- -- congestion feedback.- bytesInFlight :: Int- -- | Maximum number of bytes-in-flight that may be sent.- , congestionWindow :: Int- -- | The time when QUIC first detects congestion due to loss or ECN,- -- causing it to enter congestion recovery. When a packet sent- -- after this time is acknowledged, QUIC exits congestion- -- recovery.- , congestionRecoveryStartTime :: Maybe TimeMicrosecond- -- | Slow start threshold in bytes. When the congestion window is- -- below ssthresh, the mode is slow start and the window grows by- -- the number of bytes acknowledged.- , ssthresh :: Int- -- | Records number of bytes acked, and used for incrementing- -- the congestion window during congestion avoidance.- , bytesAcked :: Int- , numOfAckEliciting :: Int- , ccMode :: CCMode- } deriving Show+data CC = CC+ { bytesInFlight :: Int+ -- ^ The sum of the size in bytes of all sent packets that contain+ -- at least one ack-eliciting or PADDING frame, and have not been+ -- acked or declared lost. The size does not include IP or UDP+ -- overhead, but does include the QUIC header and AEAD overhead.+ -- Packets only containing ACK frames do not count towards+ -- bytes_in_flight to ensure congestion control does not impede+ -- congestion feedback.+ , congestionWindow :: Int+ -- ^ Maximum number of bytes-in-flight that may be sent.+ , congestionRecoveryStartTime :: Maybe TimeMicrosecond+ -- ^ The time when QUIC first detects congestion due to loss or ECN,+ -- causing it to enter congestion recovery. When a packet sent+ -- after this time is acknowledged, QUIC exits congestion+ -- recovery.+ , ssthresh :: Int+ -- ^ Slow start threshold in bytes. When the congestion window is+ -- below ssthresh, the mode is slow start and the window grows by+ -- the number of bytes acknowledged.+ , bytesAcked :: Int+ -- ^ Records number of bytes acked, and used for incrementing+ -- the congestion window during congestion avoidance.+ , numOfAckEliciting :: Int+ , ccMode :: CCMode+ }+ deriving (Show) initialCC :: CC-initialCC = CC {- bytesInFlight = 0- , congestionWindow = 0- , congestionRecoveryStartTime = Nothing- , ssthresh = maxBound- , bytesAcked = 0- , numOfAckEliciting = 0- , ccMode = SlowStart- }+initialCC =+ CC+ { bytesInFlight = 0+ , congestionWindow = 0+ , congestionRecoveryStartTime = Nothing+ , ssthresh = maxBound+ , bytesAcked = 0+ , numOfAckEliciting = 0+ , ccMode = SlowStart+ } ---------------------------------------------------------------- -data LossDetection = LossDetection {- largestAckedPacket :: PacketNumber- , previousAckInfo :: AckInfo- , timeOfLastAckElicitingPacket :: TimeMicrosecond- , lossTime :: Maybe TimeMicrosecond- } deriving Show+data LossDetection = LossDetection+ { largestAckedPacket :: PacketNumber+ , previousAckInfo :: AckInfo+ , timeOfLastAckElicitingPacket :: TimeMicrosecond+ , lossTime :: Maybe TimeMicrosecond+ }+ deriving (Show) initialLossDetection :: LossDetection initialLossDetection = LossDetection (-1) ackInfo0 timeMicrosecond0 Nothing ---------------------------------------------------------------- -newtype MetricsDiff = MetricsDiff [(String,Int)]+newtype MetricsDiff = MetricsDiff [(String, Int)] ---------------------------------------------------------------- -data TimerType = LossTime- | PTO- deriving Eq+data TimerType+ = LossTime+ | PTO+ deriving (Eq) instance Show TimerType where show LossTime = "loss_time"- show PTO = "pto"+ show PTO = "pto" data TimerExpired = TimerExpired data TimerCancelled = TimerCancelled -data TimerInfo = TimerInfo {- timerTime :: TimeMicrosecond- , timerLevel :: EncryptionLevel- , timerType :: TimerType- } deriving (Eq, Show)+data TimerInfo = TimerInfo+ { timerTime :: TimeMicrosecond+ , timerLevel :: EncryptionLevel+ , timerType :: TimerType+ }+ deriving (Eq, Show) -data TimerInfoQ = Empty- | Next TimerInfo- deriving (Eq)+data TimerInfoQ+ = Empty+ | Next TimerInfo+ deriving (Eq) ---------------------------------------------------------------- @@ -231,8 +252,8 @@ i2 <- newIORef False i3 <- newIORef False i4 <- newIORef False- let lst = [(InitialLevel,i1),(RTT0Level,i2),(HandshakeLevel,i3),(RTT1Level,i4)]- arr = array (InitialLevel,RTT1Level) lst+ let lst = [(InitialLevel, i1), (RTT0Level, i2), (HandshakeLevel, i3), (RTT1Level, i4)]+ arr = array (InitialLevel, RTT1Level) lst return arr makeSentPackets :: IO (Array EncryptionLevel (IORef SentPackets))@@ -240,8 +261,8 @@ i1 <- newIORef emptySentPackets i2 <- newIORef emptySentPackets i3 <- newIORef emptySentPackets- let lst = [(InitialLevel,i1),(HandshakeLevel,i2),(RTT1Level,i3)]- arr = array (InitialLevel,RTT1Level) lst+ let lst = [(InitialLevel, i1), (HandshakeLevel, i2), (RTT1Level, i3)]+ arr = array (InitialLevel, RTT1Level) lst return arr makeLossDetection :: IO (Array EncryptionLevel (IORef LossDetection))@@ -249,32 +270,32 @@ i1 <- newIORef initialLossDetection i2 <- newIORef initialLossDetection i3 <- newIORef initialLossDetection- let lst = [(InitialLevel,i1),(HandshakeLevel,i2),(RTT1Level,i3)]- arr = array (InitialLevel,RTT1Level) lst+ let lst = [(InitialLevel, i1), (HandshakeLevel, i2), (RTT1Level, i3)]+ arr = array (InitialLevel, RTT1Level) lst return arr -data LDCC = LDCC {- ldccState :: ConnState- , ldccQlogger :: QLogger- , putRetrans :: PlainPacket -> IO ()- , recoveryRTT :: IORef RTT- , recoveryCC :: TVar CC- , spaceDiscarded :: Array EncryptionLevel (IORef Bool)- , sentPackets :: Array EncryptionLevel (IORef SentPackets)- , lossDetection :: Array EncryptionLevel (IORef LossDetection)- -- The current timer key- , timerKey :: IORef (Maybe TimeoutKey)- -- The current timer value- , timerInfo :: IORef (Maybe TimerInfo)- , lostCandidates :: TVar SentPackets- , ptoPing :: TVar (Maybe EncryptionLevel)- , speedingUp :: IORef Bool- , pktNumPersistent :: IORef PacketNumber- , peerPacketNumbers :: Array EncryptionLevel (IORef PeerPacketNumbers)- , previousRTT1PPNs :: IORef PeerPacketNumbers -- for RTT1- -- Pending timer value- , timerInfoQ :: TVar TimerInfoQ- }+data LDCC = LDCC+ { ldccState :: ConnState+ , ldccQlogger :: QLogger+ , putRetrans :: PlainPacket -> IO ()+ , recoveryRTT :: IORef RTT+ , recoveryCC :: TVar CC+ , spaceDiscarded :: Array EncryptionLevel (IORef Bool)+ , sentPackets :: Array EncryptionLevel (IORef SentPackets)+ , lossDetection :: Array EncryptionLevel (IORef LossDetection)+ , -- The current timer key+ timerKey :: IORef (Maybe TimeoutKey)+ , -- The current timer value+ timerInfo :: IORef (Maybe TimerInfo)+ , lostCandidates :: TVar SentPackets+ , ptoPing :: TVar (Maybe EncryptionLevel)+ , speedingUp :: IORef Bool+ , pktNumPersistent :: IORef PacketNumber+ , peerPacketNumbers :: Array EncryptionLevel (IORef PeerPacketNumbers)+ , previousRTT1PPNs :: IORef PeerPacketNumbers -- for RTT1+ -- Pending timer value+ , timerInfoQ :: TVar TimerInfoQ+ } makePPN :: IO (Array EncryptionLevel (IORef PeerPacketNumbers)) makePPN = do@@ -282,62 +303,81 @@ ref2 <- newIORef emptyPeerPacketNumbers ref3 <- newIORef emptyPeerPacketNumbers -- using the ref for RTT0Level and RTT1Level- let lst = [(InitialLevel, ref1)- ,(RTT0Level, ref3)- ,(HandshakeLevel, ref2)- ,(RTT1Level, ref3)]- arr = array (InitialLevel,RTT1Level) lst+ let lst =+ [ (InitialLevel, ref1)+ , (RTT0Level, ref3)+ , (HandshakeLevel, ref2)+ , (RTT1Level, ref3)+ ]+ arr = array (InitialLevel, RTT1Level) lst return arr newLDCC :: ConnState -> QLogger -> (PlainPacket -> IO ()) -> IO LDCC-newLDCC cs qLog put = LDCC cs qLog put- <$> newIORef initialRTT- <*> newTVarIO initialCC- <*> makeSpaceDiscarded- <*> makeSentPackets- <*> makeLossDetection- <*> newIORef Nothing- <*> newIORef Nothing- <*> newTVarIO emptySentPackets- <*> newTVarIO Nothing- <*> newIORef False- <*> newIORef maxBound- <*> makePPN- <*> newIORef emptyPeerPacketNumbers- <*> newTVarIO Empty+newLDCC cs qLog put =+ LDCC cs qLog put+ <$> newIORef initialRTT+ <*> newTVarIO initialCC+ <*> makeSpaceDiscarded+ <*> makeSentPackets+ <*> makeLossDetection+ <*> newIORef Nothing+ <*> newIORef Nothing+ <*> newTVarIO emptySentPackets+ <*> newTVarIO Nothing+ <*> newIORef False+ <*> newIORef maxBound+ <*> makePPN+ <*> newIORef emptyPeerPacketNumbers+ <*> newTVarIO Empty instance KeepQlog LDCC where keepQlog = ldccQlogger instance Connector LDCC where- getRole = role . ldccState+ getRole = role . ldccState getEncryptionLevel = readTVarIO . encryptionLevel . ldccState- getMaxPacketSize = readIORef . maxPacketSize . ldccState+ getMaxPacketSize = readIORef . maxPacketSize . ldccState getConnectionState = readTVarIO . connectionState . ldccState- getPacketNumber = readIORef . packetNumber . ldccState- getAlive = readIORef . connectionAlive . ldccState+ getPacketNumber = readIORef . packetNumber . ldccState+ getAlive = readIORef . connectionAlive . ldccState ---------------------------------------------------------------- instance Qlog SentPacket where- qlog SentPacket{..} = "{\"raw\":{\"length\":" <> sw spSentBytes <> "},\"header\":{\"packet_type\":\"" <> toLogStr (packetType hdr) <> "\",\"packet_number\":\"" <> sw plainPacketNumber <> "\",\"dcid\":\"" <> sw (headerMyCID hdr) <> "\"},\"frames\":" <> "[" <> foldr (<>) "" (intersperse "," (map qlog plainFrames)) <> "]" <> "}"+ qlog SentPacket{..} =+ "{\"raw\":{\"length\":"+ <> sw spSentBytes+ <> "},\"header\":{\"packet_type\":\""+ <> toLogStr (packetType hdr)+ <> "\",\"packet_number\":\""+ <> sw plainPacketNumber+ <> "\",\"dcid\":\""+ <> sw (headerMyCID hdr)+ <> "\"},\"frames\":"+ <> "["+ <> foldr (<>) "" (intersperse "," (map qlog plainFrames))+ <> "]"+ <> "}" where PlainPacket hdr Plain{..} = spPlainPacket instance Qlog LostPacket where qlog (LostPacket SentPacket{..}) =- "{\"header\":{\"packet_type\":\"" <> toLogStr (packetType hdr) <> "\"" <>- ",\"packet_number\":" <> sw spPacketNumber <>- "}}"+ "{\"header\":{\"packet_type\":\""+ <> toLogStr (packetType hdr)+ <> "\""+ <> ",\"packet_number\":"+ <> sw spPacketNumber+ <> "}}" where PlainPacket hdr _ = spPlainPacket instance Qlog MetricsDiff where qlog (MetricsDiff []) = "{}"- qlog (MetricsDiff (x:xs)) = "{" <> tv0 x <> foldr tv "" xs <> "}"+ qlog (MetricsDiff (x : xs)) = "{" <> tv0 x <> foldr tv "" xs <> "}" where- tv0 (tag,val) = "\"" <> toLogStr tag <> "\":" <> sw val- tv (tag,val) pre = ",\"" <> toLogStr tag <> "\":" <> sw val <> pre+ tv0 (tag, val) = "\"" <> toLogStr tag <> "\":" <> sw val+ tv (tag, val) pre = ",\"" <> toLogStr tag <> "\":" <> sw val <> pre instance Qlog CCMode where qlog mode = "{\"new\":\"" <> sw mode <> "\"}"@@ -346,20 +386,26 @@ qlog TimerCancelled = "{\"event_type\":\"cancelled\"}" instance Qlog TimerExpired where- qlog TimerExpired = "{\"event_type\":\"expired\"}"+ qlog TimerExpired = "{\"event_type\":\"expired\"}" -instance Qlog (TimerInfo,Microseconds) where- qlog (TimerInfo{..},us) = "{\"event_type\":\"set\"" <>- ",\"timer_type\":\"" <> sw timerType <> "\"" <>- ",\"packet_number_space\":\"" <> packetNumberSpace timerLevel <> "\"" <>- ",\"delta\":" <> delta us <>- "}"+instance Qlog (TimerInfo, Microseconds) where+ qlog (TimerInfo{..}, us) =+ "{\"event_type\":\"set\""+ <> ",\"timer_type\":\""+ <> sw timerType+ <> "\""+ <> ",\"packet_number_space\":\""+ <> packetNumberSpace timerLevel+ <> "\""+ <> ",\"delta\":"+ <> delta us+ <> "}" packetNumberSpace :: EncryptionLevel -> LogStr-packetNumberSpace InitialLevel = "initial"-packetNumberSpace RTT0Level = "application_data"+packetNumberSpace InitialLevel = "initial"+packetNumberSpace RTT0Level = "application_data" packetNumberSpace HandshakeLevel = "handshake"-packetNumberSpace RTT1Level = "application_data"+packetNumberSpace RTT1Level = "application_data" delta :: Microseconds -> LogStr delta (Microseconds n) = sw n@@ -382,7 +428,7 @@ tim <- getTimeMicrosecond keepQlog q $ QCongestionStateUpdated (qlog mode) tim -qlogLossTimerUpdated :: KeepQlog q => q -> (TimerInfo,Microseconds) -> IO ()+qlogLossTimerUpdated :: KeepQlog q => q -> (TimerInfo, Microseconds) -> IO () qlogLossTimerUpdated q tmi = do tim <- getTimeMicrosecond keepQlog q $ QLossTimerUpdated (qlog tmi) tim
Network/QUIC/Recovery/Utils.hs view
@@ -2,17 +2,17 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Recovery.Utils (- retransmit- , sendPing- , mergeLostCandidates- , mergeLostCandidatesAndClear- , peerCompletedAddressValidation- , countAckEli- , inCongestionRecovery- , delay- ) where+ retransmit,+ sendPing,+ mergeLostCandidates,+ mergeLostCandidatesAndClear,+ peerCompletedAddressValidation,+ countAckEli,+ inCongestionRecovery,+ delay,+) where -import Data.Sequence (Seq, (<|), ViewL(..))+import Data.Sequence (Seq, ViewL (..), (<|)) import qualified Data.Sequence as Seq import UnliftIO.Concurrent import UnliftIO.STM@@ -26,8 +26,8 @@ retransmit :: LDCC -> Seq SentPacket -> IO () retransmit ldcc lostPackets- | null packetsToBeResent = getEncryptionLevel ldcc >>= sendPing ldcc- | otherwise = mapM_ put packetsToBeResent+ | null packetsToBeResent = getEncryptionLevel ldcc >>= sendPing ldcc+ | otherwise = mapM_ put packetsToBeResent where packetsToBeResent = Seq.filter spAckEliciting lostPackets put = putRetrans ldcc . spPlainPacket@@ -37,9 +37,10 @@ sendPing :: LDCC -> EncryptionLevel -> IO () sendPing LDCC{..} lvl = do now <- getTimeMicrosecond- atomicModifyIORef'' (lossDetection ! lvl) $ \ld -> ld {- timeOfLastAckElicitingPacket = now- }+ atomicModifyIORef'' (lossDetection ! lvl) $ \ld ->+ ld+ { timeOfLastAckElicitingPacket = now+ } atomically $ writeTVar ptoPing $ Just lvl ----------------------------------------------------------------@@ -58,12 +59,12 @@ merge :: Seq SentPacket -> Seq SentPacket -> Seq SentPacket merge s1 s2 = case Seq.viewl s1 of- EmptyL -> s2- x :< s1' -> case Seq.viewl s2 of- EmptyL -> s1- y :< s2'- | spPacketNumber x < spPacketNumber y -> x <| merge s1' s2- | otherwise -> y <| merge s1 s2'+ EmptyL -> s2+ x :< s1' -> case Seq.viewl s2 of+ EmptyL -> s1+ y :< s2'+ | spPacketNumber x < spPacketNumber y -> x <| merge s1' s2+ | otherwise -> y <| merge s1 s2' ---------------------------------------------------------------- @@ -75,7 +76,7 @@ peerCompletedAddressValidation :: LDCC -> IO Bool -- For servers: assume clients validate the server's address implicitly. peerCompletedAddressValidation ldcc- | isServer ldcc = return True+ | isServer ldcc = return True -- For clients: servers complete address validation when a protected -- packet is received. peerCompletedAddressValidation ldcc = isConnectionEstablished ldcc@@ -84,8 +85,8 @@ countAckEli :: SentPacket -> Int countAckEli sentPacket- | spAckEliciting sentPacket = 1- | otherwise = 0+ | spAckEliciting sentPacket = 1+ | otherwise = 0 ----------------------------------------------------------------
Network/QUIC/Sender.hs view
@@ -1,10 +1,10 @@ {-# LANGUAGE OverloadedStrings #-} module Network.QUIC.Sender (- sender- , mkHeader- , sendFinal- ) where+ sender,+ mkHeader,+ sendFinal,+) where import qualified Data.ByteString as BS import Foreign.Ptr (plusPtr)@@ -30,10 +30,10 @@ let len = BS.length crypto mstrm <- getCryptoStream conn lvl case mstrm of- Nothing -> E.throwIO MustNotReached- Just strm -> do- off <- getTxStreamOffset strm len- return $ CryptoF off crypto+ Nothing -> E.throwIO MustNotReached+ Just strm -> do+ off <- getTxStreamOffset strm len+ return $ CryptoF off crypto ---------------------------------------------------------------- @@ -44,141 +44,152 @@ SizedBuffer buf0 bufsiz0 = encryptRes conn ldcc = connLDCC conn go maxSiz = do- mx <- atomically ((Just <$> takePingSTM ldcc)- `orElse` (Nothing <$ checkWindowOpenSTM ldcc maxSiz))+ mx <-+ atomically+ ( (Just <$> takePingSTM ldcc)+ `orElse` (Nothing <$ checkWindowOpenSTM ldcc maxSiz)+ ) case mx of- Just lvl | lvl `elem` [InitialLevel,HandshakeLevel] -> do- sendPingPacket conn lvl- go maxSiz- _ -> do- when (isJust mx) $ qlogDebug conn $ Debug "probe new"- (sentPackets, leftsiz) <- buildPackets buf0 bufsiz0 maxSiz spkts0 id- let bytes = bufsiz0 - leftsiz- when (isServer conn) $ waitAntiAmplificationFree conn bytes- now <- getTimeMicrosecond- connSend conn buf0 bytes- addTxBytes conn bytes- forM_ sentPackets $ \sentPacket0 -> do- let sentPacket = sentPacket0 { spTimeSent = now }- qlogSent conn sentPacket now- onPacketSent ldcc sentPacket+ Just lvl | lvl `elem` [InitialLevel, HandshakeLevel] -> do+ sendPingPacket conn lvl+ go maxSiz+ _ -> do+ when (isJust mx) $ qlogDebug conn $ Debug "probe new"+ (sentPackets, leftsiz) <- buildPackets buf0 bufsiz0 maxSiz spkts0 id+ let bytes = bufsiz0 - leftsiz+ when (isServer conn) $ waitAntiAmplificationFree conn bytes+ now <- getTimeMicrosecond+ connSend conn buf0 bytes+ addTxBytes conn bytes+ forM_ sentPackets $ \sentPacket0 -> do+ let sentPacket = sentPacket0{spTimeSent = now}+ qlogSent conn sentPacket now+ onPacketSent ldcc sentPacket buildPackets _ _ _ [] _ = error "sendPacket: buildPackets" buildPackets buf bufsiz siz [spkt] build0 = do let pkt = spPlainPacket spkt- (bytes,padlen) <- encodePlainPacket conn (SizedBuffer buf bufsiz) pkt $ Just siz- if bytes < 0 then- return (build0 [], bufsiz)- else do- let sentPacket = fixSentPacket spkt bytes padlen- return (build0 [sentPacket], bufsiz - bytes)- buildPackets buf bufsiz siz (spkt:spkts) build0 = do+ (bytes, padlen) <-+ encodePlainPacket conn (SizedBuffer buf bufsiz) pkt $ Just siz+ if bytes < 0+ then return (build0 [], bufsiz)+ else do+ let sentPacket = fixSentPacket spkt bytes padlen+ return (build0 [sentPacket], bufsiz - bytes)+ buildPackets buf bufsiz siz (spkt : spkts) build0 = do let pkt = spPlainPacket spkt- (bytes,padlen) <- encodePlainPacket conn (SizedBuffer buf bufsiz) pkt Nothing- if bytes < 0 then- buildPackets buf bufsiz siz spkts build0- else do- let sentPacket = fixSentPacket spkt bytes padlen- let build0' = build0 . (sentPacket :)- buf' = buf `plusPtr` bytes- bufsiz' = bufsiz - bytes- siz' = siz - spSentBytes sentPacket- buildPackets buf' bufsiz' siz' spkts build0'+ (bytes, padlen) <- encodePlainPacket conn (SizedBuffer buf bufsiz) pkt Nothing+ if bytes < 0+ then buildPackets buf bufsiz siz spkts build0+ else do+ let sentPacket = fixSentPacket spkt bytes padlen+ let build0' = build0 . (sentPacket :)+ buf' = buf `plusPtr` bytes+ bufsiz' = bufsiz - bytes+ siz' = siz - spSentBytes sentPacket+ buildPackets buf' bufsiz' siz' spkts build0' ---------------------------------------------------------------- sendPingPacket :: Connection -> EncryptionLevel -> IO () sendPingPacket conn lvl = do maxSiz <- getMaxPacketSize conn- ok <- if isClient conn then return True- else checkAntiAmplificationFree conn maxSiz+ ok <-+ if isClient conn+ then return True+ else checkAntiAmplificationFree conn maxSiz when ok $ do let ldcc = connLDCC conn mp <- releaseOldest ldcc lvl frames <- case mp of- Nothing -> do- qlogDebug conn $ Debug "probe ping"- return [Ping]- Just spkt -> do- qlogDebug conn $ Debug "probe old"- let PlainPacket _ plain0 = spPlainPacket spkt- adjustForRetransmit conn $ plainFrames plain0+ Nothing -> do+ qlogDebug conn $ Debug "probe ping"+ return [Ping]+ Just spkt -> do+ qlogDebug conn $ Debug "probe old"+ let PlainPacket _ plain0 = spPlainPacket spkt+ adjustForRetransmit conn $ plainFrames plain0 xs <- construct conn lvl frames- if null xs then- qlogDebug conn $ Debug "ping NULL"- else do- let spkt = last xs- ping = spPlainPacket spkt- let sizbuf@(SizedBuffer buf _) = encryptRes conn- (bytes,padlen) <- encodePlainPacket conn sizbuf ping (Just maxSiz)- when (bytes >= 0) $ do- now <- getTimeMicrosecond- connSend conn buf bytes- addTxBytes conn bytes- let sentPacket0 = fixSentPacket spkt bytes padlen- sentPacket = sentPacket0 { spTimeSent = now }- qlogSent conn sentPacket now- onPacketSent ldcc sentPacket+ if null xs+ then qlogDebug conn $ Debug "ping NULL"+ else do+ let spkt = last xs+ ping = spPlainPacket spkt+ let sizbuf@(SizedBuffer buf _) = encryptRes conn+ (bytes, padlen) <- encodePlainPacket conn sizbuf ping (Just maxSiz)+ when (bytes >= 0) $ do+ now <- getTimeMicrosecond+ connSend conn buf bytes+ addTxBytes conn bytes+ let sentPacket0 = fixSentPacket spkt bytes padlen+ sentPacket = sentPacket0{spTimeSent = now}+ qlogSent conn sentPacket now+ onPacketSent ldcc sentPacket ---------------------------------------------------------------- -construct :: Connection- -> EncryptionLevel- -> [Frame]- -> IO [SentPacket]+construct+ :: Connection+ -> EncryptionLevel+ -> [Frame]+ -> IO [SentPacket] construct conn lvl frames = do discarded <- getPacketNumberSpaceDiscarded ldcc lvl- if discarded then- return []- else do- established <- isConnectionEstablished conn- if established || (isServer conn && lvl == HandshakeLevel) then do- constructTargetPacket- else do- ppkt0 <- constructLowerAckPacket- ppkt1 <- constructTargetPacket- return (ppkt0 ++ ppkt1)+ if discarded+ then return []+ else do+ established <- isConnectionEstablished conn+ if established || (isServer conn && lvl == HandshakeLevel)+ then do+ constructTargetPacket+ else do+ ppkt0 <- constructLowerAckPacket+ ppkt1 <- constructTargetPacket+ return (ppkt0 ++ ppkt1) where ldcc = connLDCC conn constructLowerAckPacket = do let lvl' = case lvl of- HandshakeLevel -> InitialLevel- RTT1Level -> HandshakeLevel- _ -> RTT1Level- if lvl' == RTT1Level then- return []- else do- ppns <- getPeerPacketNumbers ldcc lvl'- if nullPeerPacketNumbers ppns then- return []- else- mkPlainPacket conn lvl' [] ppns+ HandshakeLevel -> InitialLevel+ RTT1Level -> HandshakeLevel+ _ -> RTT1Level+ if lvl' == RTT1Level+ then return []+ else do+ ppns <- getPeerPacketNumbers ldcc lvl'+ if nullPeerPacketNumbers ppns+ then return []+ else mkPlainPacket conn lvl' [] ppns constructTargetPacket- | null frames = do -- ACK only packet+ | null frames = do+ -- ACK only packet resetDealyedAck conn ppns <- getPeerPacketNumbers ldcc lvl- if nullPeerPacketNumbers ppns then- return []- else- if lvl == RTT1Level then do- prevppns <- getPreviousRTT1PPNs ldcc- if ppns /= prevppns then do- setPreviousRTT1PPNs ldcc ppns- mkPlainPacket conn lvl [] ppns- else- return []- else- mkPlainPacket conn lvl [] ppns- | otherwise = do+ if nullPeerPacketNumbers ppns+ then return []+ else+ if lvl == RTT1Level+ then do+ prevppns <- getPreviousRTT1PPNs ldcc+ if ppns /= prevppns+ then do+ setPreviousRTT1PPNs ldcc ppns+ mkPlainPacket conn lvl [] ppns+ else return []+ else mkPlainPacket conn lvl [] ppns+ | otherwise = do resetDealyedAck conn ppns <- getPeerPacketNumbers ldcc lvl mkPlainPacket conn lvl frames ppns -mkPlainPacket :: Connection -> EncryptionLevel -> [Frame] -> PeerPacketNumbers -> IO [SentPacket]+mkPlainPacket+ :: Connection -> EncryptionLevel -> [Frame] -> PeerPacketNumbers -> IO [SentPacket] mkPlainPacket conn lvl frames0 ppns = do- let ackEli | null frames0 = False- | otherwise = True- frames | nullPeerPacketNumbers ppns = frames0- | otherwise = mkAck ppns : frames0+ let ackEli+ | null frames0 = False+ | otherwise = True+ frames+ | nullPeerPacketNumbers ppns = frames0+ | otherwise = mkAck ppns : frames0 header <- mkHeader conn lvl mypn <- nextPacketNumber conn let convert = onPlainCreated $ connHooks conn@@ -195,16 +206,17 @@ peercid <- getPeerCID conn token <- if lvl == InitialLevel then getToken conn else return "" return $ case lvl of- InitialLevel -> Initial ver peercid mycid token- RTT0Level -> RTT0 ver peercid mycid- HandshakeLevel -> Handshake ver peercid mycid- RTT1Level -> Short peercid+ InitialLevel -> Initial ver peercid mycid token+ RTT0Level -> RTT0 ver peercid mycid+ HandshakeLevel -> Handshake ver peercid mycid+ RTT1Level -> Short peercid ---------------------------------------------------------------- -data Switch = SwPing EncryptionLevel- | SwOut Output- | SwStrm TxStreamData+data Switch+ = SwPing EncryptionLevel+ | SwOut Output+ | SwStrm TxStreamData sender :: Connection -> IO () sender conn = handleLogT logAction $ forever $ sendP conn@@ -213,13 +225,16 @@ sendP :: Connection -> IO () sendP conn = do- x <- atomically ((SwPing <$> takePingSTM (connLDCC conn))- `orElse` (SwOut <$> takeOutputSTM conn)- `orElse` (SwStrm <$> takeSendStreamQSTM conn))+ x <-+ atomically+ ( (SwPing <$> takePingSTM (connLDCC conn))+ `orElse` (SwOut <$> takeOutputSTM conn)+ `orElse` (SwStrm <$> takeSendStreamQSTM conn)+ ) case x of- SwPing lvl -> sendPingPacket conn lvl- SwOut out -> sendOutput conn out- SwStrm tx -> sendTxStreamData conn tx+ SwPing lvl -> sendPingPacket conn lvl+ SwOut out -> sendOutput conn out+ SwStrm tx -> sendTxStreamData conn tx sendFinal :: Connection -> IO () sendFinal conn = loop 30@@ -230,35 +245,34 @@ loop n = do mx <- timeout (Microseconds 10) msg $ sendP conn case mx of- Nothing -> return ()- Just () -> loop (n - 1)+ Nothing -> return ()+ Just () -> loop (n - 1) ---------------------------------------------------------------- discardClientInitialPacketNumberSpace :: Connection -> IO () discardClientInitialPacketNumberSpace conn- | isClient conn = do+ | isClient conn = do let ldcc = connLDCC conn discarded <- getAndSetPacketNumberSpaceDiscarded ldcc InitialLevel unless discarded $ fire conn (Microseconds 100000) $ do dropSecrets conn InitialLevel clearCryptoStream conn InitialLevel onPacketNumberSpaceDiscarded ldcc InitialLevel- | otherwise = return ()+ | otherwise = return () sendOutput :: Connection -> Output -> IO () sendOutput conn (OutControl lvl frames action) = do construct conn lvl frames >>= sendPacket conn when (lvl == HandshakeLevel) $ discardClientInitialPacketNumberSpace conn action- sendOutput conn (OutHandshake lcs0) = do let convert = onTLSHandshakeCreated $ connHooks conn- (lcs,wait) = convert lcs0+ (lcs, wait) = convert lcs0 -- only for h3spec when wait $ wait0RTTReady conn sendCryptoFragments conn lcs- when (any (\(l,_) -> l == HandshakeLevel) lcs) $+ when (any (\(l, _) -> l == HandshakeLevel) lcs) $ discardClientInitialPacketNumberSpace conn sendOutput conn (OutRetrans (PlainPacket hdr0 plain0)) = do frames <- adjustForRetransmit conn $ plainFrames plain0@@ -268,29 +282,29 @@ levelFromHeader :: Header -> EncryptionLevel levelFromHeader hdr | lvl == RTT0Level = RTT1Level- | otherwise = lvl+ | otherwise = lvl where lvl = packetEncryptionLevel hdr adjustForRetransmit :: Connection -> [Frame] -> IO [Frame]-adjustForRetransmit _ [] = return []-adjustForRetransmit conn (Padding{}:xs) = adjustForRetransmit conn xs-adjustForRetransmit conn (Ack{}:xs) = adjustForRetransmit conn xs-adjustForRetransmit conn (MaxStreamData sid _:xs) = do+adjustForRetransmit _ [] = return []+adjustForRetransmit conn (Padding{} : xs) = adjustForRetransmit conn xs+adjustForRetransmit conn (Ack{} : xs) = adjustForRetransmit conn xs+adjustForRetransmit conn (MaxStreamData sid _ : xs) = do mstrm <- findStream conn sid case mstrm of- Nothing -> adjustForRetransmit conn xs- Just strm -> do- newMax <- getRxMaxStreamData strm- let r = MaxStreamData sid newMax- rs <- adjustForRetransmit conn xs- return (r : rs)-adjustForRetransmit conn (MaxData{}:xs) = do+ Nothing -> adjustForRetransmit conn xs+ Just strm -> do+ newMax <- getRxMaxStreamData strm+ let r = MaxStreamData sid newMax+ rs <- adjustForRetransmit conn xs+ return (r : rs)+adjustForRetransmit conn (MaxData{} : xs) = do newMax <- getRxMaxData conn let r = MaxData newMax rs <- adjustForRetransmit conn xs return (r : rs)-adjustForRetransmit conn (x:xs) = do+adjustForRetransmit conn (x : xs) = do rs <- adjustForRetransmit conn xs return (x : rs) @@ -305,7 +319,11 @@ sendCryptoFragments conn lcs = do loop limitationC id lcs where- loop :: Int -> ([SentPacket] -> [SentPacket]) -> [(EncryptionLevel, CryptoData)] -> IO ()+ loop+ :: Int+ -> ([SentPacket] -> [SentPacket])+ -> [(EncryptionLevel, CryptoData)]+ -> IO () loop _ build0 [] = do let spkts0 = build0 [] unless (null spkts0) $ sendPacket conn spkts0@@ -334,29 +352,28 @@ ---------------------------------------------------------------- threshold :: Int-threshold = 832+threshold = 832 limitation :: Int limitation = 1040 packFin :: Connection -> Stream -> Bool -> IO Bool-packFin _ _ True = return True+packFin _ _ True = return True packFin conn s False = do mx <- tryPeekSendStreamQ conn case mx of- Just (TxStreamData s1 [] 0 True)- | streamId s == streamId s1 -> do+ Just (TxStreamData s1 [] 0 True)+ | streamId s == streamId s1 -> do _ <- takeSendStreamQ conn return True- _ -> return False+ _ -> return False sendTxStreamData :: Connection -> TxStreamData -> IO () sendTxStreamData conn (TxStreamData s dats len fin0) = do fin <- packFin conn s fin0- if len < limitation then- sendStreamSmall conn s dats fin len- else- sendStreamLarge conn s dats fin+ if len < limitation+ then sendStreamSmall conn s dats fin len+ else sendStreamLarge conn s dats fin sendStreamSmall :: Connection -> Stream -> [StreamData] -> Bool -> Int -> IO () sendStreamSmall conn s0 dats0 fin0 len0 = do@@ -364,9 +381,10 @@ let sid0 = streamId s0 frame0 = StreamF sid0 off0 dats0 fin0 sb = if fin0 then (s0 :) else id- (frames,streams) <- loop s0 frame0 len0 id sb+ (frames, streams) <- loop s0 frame0 len0 id sb ready <- isConnection1RTTReady conn- let lvl | ready = RTT1Level+ let lvl+ | ready = RTT1Level | otherwise = RTT0Level construct conn lvl frames >>= sendPacket conn mapM_ syncFinTx streams@@ -374,40 +392,44 @@ tryPeek = do mx <- tryPeekSendStreamQ conn case mx of- Nothing -> do- yield- tryPeekSendStreamQ conn- Just _ -> return mx- loop :: Stream -> Frame -> Int- -> ([Frame] -> [Frame])- -> ([Stream] -> [Stream])- -> IO ([Frame], [Stream])+ Nothing -> do+ yield+ tryPeekSendStreamQ conn+ Just _ -> return mx+ loop+ :: Stream+ -> Frame+ -> Int+ -> ([Frame] -> [Frame])+ -> ([Stream] -> [Stream])+ -> IO ([Frame], [Stream]) loop s frame total build sb = do mx <- tryPeek case mx of- Nothing -> return (build [frame], sb [])- Just (TxStreamData s1 dats1 len1 fin1) -> do- let total1 = len1 + total- if total1 < limitation then do- _ <- takeSendStreamQ conn -- cf tryPeek- fin1' <- packFin conn s fin1 -- must be after takeSendStreamQ- off1 <- getTxStreamOffset s1 len1- let sid = streamId s- sid1 = streamId s1- if sid == sid1 then do- let (off,dats) = case frame of- StreamF _ o d _ -> (o,d)- _ -> error "sendStreamSmall"- frame1 = StreamF sid off (dats ++ dats1) fin1'- sb1 = if fin1 then (sb . (s1 :)) else sb- loop s1 frame1 total1 build sb1- else do- let frame1 = StreamF sid1 off1 dats1 fin1'- build1 = build . (frame :)- sb1 = if fin1 then (sb . (s1 :)) else sb- loop s1 frame1 total1 build1 sb1- else- return (build [frame], sb [])+ Nothing -> return (build [frame], sb [])+ Just (TxStreamData s1 dats1 len1 fin1) -> do+ let total1 = len1 + total+ if total1 < limitation+ then do+ _ <- takeSendStreamQ conn -- cf tryPeek+ fin1' <- packFin conn s fin1 -- must be after takeSendStreamQ+ off1 <- getTxStreamOffset s1 len1+ let sid = streamId s+ sid1 = streamId s1+ if sid == sid1+ then do+ let (off, dats) = case frame of+ StreamF _ o d _ -> (o, d)+ _ -> error "sendStreamSmall"+ frame1 = StreamF sid off (dats ++ dats1) fin1'+ sb1 = if fin1 then (sb . (s1 :)) else sb+ loop s1 frame1 total1 build sb1+ else do+ let frame1 = StreamF sid1 off1 dats1 fin1'+ build1 = build . (frame :)+ sb1 = if fin1 then (sb . (s1 :)) else sb+ loop s1 frame1 total1 build1 sb1+ else return (build [frame], sb []) sendStreamLarge :: Connection -> Stream -> [ByteString] -> Bool -> IO () sendStreamLarge conn s dats0 fin0 = do@@ -417,30 +439,32 @@ sid = streamId s loop [] = return () loop dats = do- let (dats1,dats2) = splitChunks dats+ let (dats1, dats2) = splitChunks dats len = totalLen dats1 off <- getTxStreamOffset s len let fin = fin0 && null dats2 frame = StreamF sid off dats1 fin ready <- isConnection1RTTReady conn- let lvl | ready = RTT1Level+ let lvl+ | ready = RTT1Level | otherwise = RTT0Level construct conn lvl [frame] >>= sendPacket conn loop dats2 -- Typical case: [3, 1024, 1024, 1024, 200]-splitChunks :: [ByteString] -> ([ByteString],[ByteString])+splitChunks :: [ByteString] -> ([ByteString], [ByteString]) splitChunks bs0 = loop bs0 0 id where- loop [] _ build = let curr = build [] in (curr, [])- loop bbs@(b:bs) siz0 build- | siz <= threshold = let build' = build . (b :) in loop bs siz build'- | siz <= limitation = let curr = build [b] in (curr, bs)- | len > limitation = let (u,b') = BS.splitAt (limitation - siz0) b- curr = build [u]- bs' = b':bs- in (curr,bs')- | otherwise = let curr = build [] in (curr, bbs)+ loop [] _ build = let curr = build [] in (curr, [])+ loop bbs@(b : bs) siz0 build+ | siz <= threshold = let build' = build . (b :) in loop bs siz build'+ | siz <= limitation = let curr = build [b] in (curr, bs)+ | len > limitation =+ let (u, b') = BS.splitAt (limitation - siz0) b+ curr = build [u]+ bs' = b' : bs+ in (curr, bs')+ | otherwise = let curr = build [] in (curr, bbs) where len = BS.length b siz = siz0 + len
Network/QUIC/Server.hs view
@@ -1,25 +1,27 @@ -- | This main module provides APIs for QUIC servers. module Network.QUIC.Server (- -- * Running a QUIC server- run- , stop- -- * Configuration- , ServerConfig- , defaultServerConfig- , scAddresses- , scALPN- , scRequireRetry- , scUse0RTT- , scCiphers- , scGroups- , scVersions--- , scParameters- , scCredentials- , scSessionManager- -- * Certificate- , clientCertificateChain- ) where+ -- * Running a QUIC server+ run,+ stop, + -- * Configuration+ ServerConfig,+ defaultServerConfig,+ scAddresses,+ scALPN,+ scRequireRetry,+ scUse0RTT,+ scCiphers,+ scGroups,+ scVersions,+ -- , scParameters+ scCredentials,+ scSessionManager,++ -- * Certificate+ clientCertificateChain,+) where+ import Data.X509 (CertificateChain) import Network.QUIC.Config@@ -32,5 +34,5 @@ -- | Getting a certificate chain of a client. clientCertificateChain :: Connection -> IO (Maybe CertificateChain) clientCertificateChain conn- | isClient conn = return Nothing- | otherwise = getCertificateChain conn+ | isClient conn = return Nothing+ | otherwise = getCertificateChain conn
Network/QUIC/Server/Reader.hs view
@@ -23,10 +23,10 @@ import qualified Data.ByteString as BS import Data.Map.Strict (Map) import qualified Data.Map.Strict as M-import Data.OrdPSQ (OrdPSQ)-import qualified Data.OrdPSQ as PSQ import qualified GHC.IO.Exception as E import Network.ByteOrder+import Network.Control (LRUCache)+import qualified Network.Control as LRUCache import Network.UDP (ListenSocket, UDPSocket, ClientSockAddr) import qualified Network.UDP as UDP import qualified System.IO.Error as E@@ -91,29 +91,25 @@ ---------------------------------------------------------------- -- Original destination CID -> RecvQ-data RecvQDict = RecvQDict Int (OrdPSQ CID Int RecvQ)+data RecvQDict = RecvQDict(LRUCache CID RecvQ) recvQDictSize :: Int recvQDictSize = 100 emptyRecvQDict :: RecvQDict-emptyRecvQDict = RecvQDict 0 PSQ.empty+emptyRecvQDict = RecvQDict $ LRUCache.empty recvQDictSize lookupRecvQDict :: IORef RecvQDict -> CID -> IO (Maybe RecvQ) lookupRecvQDict ref dcid = do- RecvQDict _ qt <- readIORef ref- return $ case PSQ.lookup dcid qt of- Nothing -> Nothing- Just (_,q) -> Just q+ RecvQDict c <- readIORef ref+ return $ case LRUCache.lookup dcid c of+ Nothing -> Nothing+ Just q -> Just q insertRecvQDict :: IORef RecvQDict -> CID -> RecvQ -> IO () insertRecvQDict ref dcid q = atomicModifyIORef'' ref ins where- ins (RecvQDict p qt0) = let qt1 | PSQ.size qt0 <= recvQDictSize = qt0- | otherwise = PSQ.deleteMin qt0- qt2 = PSQ.insert dcid p q qt1- p' = p + 1 -- fixme: overflow- in RecvQDict p' qt2+ ins (RecvQDict c) = RecvQDict $ LRUCache.insert dcid q c ----------------------------------------------------------------
Network/QUIC/Stream.hs view
@@ -1,50 +1,46 @@ module Network.QUIC.Stream (- -- * Types- Stream- , streamId- , streamConnection- , newStream- , TxStreamData(..)- , Flow(..)- , defaultFlow- , StreamState(..)- , RecvStreamQ(..)- , RxStreamData(..)- , Length- , syncFinTx- , waitFinTx- -- * Misc- , getTxStreamOffset- , isTxStreamClosed- , setTxStreamClosed- , getRxStreamOffset- , isRxStreamClosed- , setRxStreamClosed- , readStreamFlowTx- , addTxStreamData- , setTxMaxStreamData- , readStreamFlowRx- , addRxStreamData- , setRxMaxStreamData- , addRxMaxStreamData- , getRxMaxStreamData- , getRxStreamWindow- , flowWindow- -- * Reass- , takeRecvStreamQwithSize- , putRxStreamData- , FlowCntl(..)- , tryReassemble- -- * Table- , StreamTable- , emptyStreamTable- , lookupStream- , insertStream- , deleteStream- , insertCryptoStreams- , deleteCryptoStream- , lookupCryptoStream- ) where+ -- * Types+ Stream,+ streamId,+ streamConnection,+ newStream,+ TxStreamData (..),+ StreamState (..),+ RecvStreamQ (..),+ RxStreamData (..),+ Length,+ syncFinTx,+ waitFinTx,++ -- * Misc+ getTxStreamOffset,+ isTxStreamClosed,+ setTxStreamClosed,+ getRxStreamOffset,+ isRxStreamClosed,+ setRxStreamClosed,+ readStreamFlowTx,+ addTxStreamData,+ setTxMaxStreamData,+ getRxMaxStreamData,+ updateStreamFlowRx,++ -- * Reass+ takeRecvStreamQwithSize,+ putRxStreamData,+ FlowCntl (..),+ tryReassemble,++ -- * Table+ StreamTable,+ emptyStreamTable,+ lookupStream,+ insertStream,+ deleteStream,+ insertCryptoStreams,+ deleteCryptoStream,+ lookupCryptoStream,+) where import Network.QUIC.Stream.Misc import Network.QUIC.Stream.Reass
Network/QUIC/Stream/Frag.hs view
@@ -1,33 +1,41 @@ module Network.QUIC.Stream.Frag where -import Data.Sequence (Seq, viewl, viewr, ViewL(..), ViewR(..), (<|), dropWhileL)+import Data.Sequence (+ Seq,+ ViewL (..),+ ViewR (..),+ dropWhileL,+ viewl,+ viewr,+ (<|),+ ) class Frag a where currOff :: a -> Int nextOff :: a -> Int- shrink :: Int -> a -> a+ shrink :: Int -> a -> a instance Frag a => Frag (Seq a) where currOff s = case viewl s of- EmptyL -> error "Seq is empty (1)"- x :< _ -> currOff x+ EmptyL -> error "Seq is empty (1)"+ x :< _ -> currOff x nextOff s = case viewr s of- EmptyR -> error "Seq is empty (2)"- _ :> x -> nextOff x+ EmptyR -> error "Seq is empty (2)"+ _ :> x -> nextOff x shrink = shrinkSeq shrinkSeq :: Frag a => Int -> Seq a -> Seq a shrinkSeq n s0 = case viewl s of- EmptyL -> error "shrinkSeq"- x :< xs- | nextOff x == n -> xs- | otherwise -> shrink n x <| xs+ EmptyL -> error "shrinkSeq"+ x :< xs+ | nextOff x == n -> xs+ | otherwise -> shrink n x <| xs where s = dropWhileL (\y -> not (currOff y <= n && n <= nextOff y)) s0 -data F = F Int Int deriving Show+data F = F Int Int deriving (Show) instance Frag F where- currOff (F s _) = s- nextOff (F _ e) = e- shrink n (F s e) = if s <= n && n <= e then F n e else error "shrink"+ currOff (F s _) = s+ nextOff (F _ e) = e+ shrink n (F s e) = if s <= n && n <= e then F n e else error "shrink"
Network/QUIC/Stream/Misc.hs view
@@ -2,24 +2,23 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Stream.Misc (- getTxStreamOffset- , isTxStreamClosed- , setTxStreamClosed- , getRxStreamOffset- , isRxStreamClosed- , setRxStreamClosed- --- , readStreamFlowTx- , addTxStreamData- , setTxMaxStreamData- , readStreamFlowRx- , addRxStreamData- , setRxMaxStreamData- , addRxMaxStreamData- , getRxMaxStreamData- , getRxStreamWindow- ) where+ getTxStreamOffset,+ isTxStreamClosed,+ setTxStreamClosed,+ getRxStreamOffset,+ isRxStreamClosed,+ setRxStreamClosed,+ --+ readStreamFlowTx,+ addTxStreamData,+ setTxMaxStreamData,+ --+ getRxMaxStreamData,+ addRxStreamData,+ updateStreamFlowRx,+) where +import Network.Control import UnliftIO.STM import Network.QUIC.Imports@@ -66,7 +65,7 @@ ---------------------------------------------------------------- -readStreamFlowTx :: Stream -> STM Flow+readStreamFlowTx :: Stream -> STM TxFlow readStreamFlowTx Stream{..} = readTVar streamFlowTx ----------------------------------------------------------------@@ -74,38 +73,31 @@ addTxStreamData :: Stream -> Int -> STM () addTxStreamData Stream{..} n = modifyTVar' streamFlowTx add where- add flow = flow { flowData = flowData flow + n }+ add flow = flow{txfSent = txfSent flow + n} setTxMaxStreamData :: Stream -> Int -> IO () setTxMaxStreamData Stream{..} n = atomically $ modifyTVar' streamFlowTx set where set flow- | flowMaxData flow < n = flow { flowMaxData = n }- | otherwise = flow+ | txfLimit flow < n = flow{txfLimit = n}+ | otherwise = flow ---------------------------------------------------------------- +getRxMaxStreamData :: Stream -> IO Int+getRxMaxStreamData Stream{..} = rxfLimit <$> readIORef streamFlowRx+ addRxStreamData :: Stream -> Int -> IO () addRxStreamData Stream{..} n = atomicModifyIORef'' streamFlowRx add where- add flow = flow { flowData = flowData flow + n }--setRxMaxStreamData :: Stream -> Int -> IO ()-setRxMaxStreamData Stream{..} n = atomicModifyIORef'' streamFlowRx- $ \flow -> flow { flowMaxData = n }--addRxMaxStreamData :: Stream -> Int -> IO Int-addRxMaxStreamData Stream{..} n = atomicModifyIORef' streamFlowRx add- where- add flow = (flow { flowMaxData = m }, m)- where- m = flowMaxData flow + n--getRxMaxStreamData :: Stream -> IO Int-getRxMaxStreamData Stream{..} = flowMaxData <$> readIORef streamFlowRx+ add flow = flow{rxfReceived = rxfReceived flow + n} -getRxStreamWindow :: Stream -> IO Int-getRxStreamWindow Stream{..} = flowWindow <$> readIORef streamFlowRx+updateStreamFlowRx :: Stream -> Int -> IO (Maybe Int)+updateStreamFlowRx Stream{..} consumed =+ atomicModifyIORef' streamFlowRx $ maybeOpenRxWindow consumed FCTMaxData -readStreamFlowRx :: Stream -> IO Flow-readStreamFlowRx Stream{..} = readIORef streamFlowRx+{- cannot be used due to reassemble.+checkRxMaxStreamData :: Stream -> Int -> IO Bool+checkRxMaxStreamData Stream{..} len =+ atomicModifyIORef' streamFlowRx $ checkRxLimit len+-}
Network/QUIC/Stream/Reass.hs view
@@ -2,17 +2,18 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Stream.Reass (- takeRecvStreamQwithSize- , putRxStreamData- , FlowCntl(..)- , tryReassemble- ) where+ takeRecvStreamQwithSize,+ putRxStreamData,+ FlowCntl (..),+ tryReassemble,+) where +import qualified Data.ByteString as BS import Data.Sequence (Seq) import qualified Data.Sequence as Seq-import qualified Data.ByteString as BS import Network.QUIC.Imports+ -- import Network.QUIC.Logger import Network.QUIC.Stream.Frag import Network.QUIC.Stream.Misc@@ -43,47 +44,49 @@ takeRecvStreamQwithSize :: Stream -> Int -> IO ByteString takeRecvStreamQwithSize strm siz0 = do eos <- getEndOfStream strm- if eos then- return ""- else do- mb <- readPendingData strm- case mb of- Nothing -> do- b0 <- takeRecvStreamQ strm- if b0 == "" then do- setEndOfStream strm- return ""- else do- let len = BS.length b0- case len `compare` siz0 of- LT -> tryRead (siz0 - len) (b0 :)- EQ -> return b0- GT -> do- let (b1,b2) = BS.splitAt siz0 b0- writePendingData strm b2- return b1- Just b0 -> do- clearPendingData strm- let len = BS.length b0- tryRead (siz0 - len) (b0 :)+ if eos+ then return ""+ else do+ mb <- readPendingData strm+ case mb of+ Nothing -> do+ b0 <- takeRecvStreamQ strm+ if b0 == ""+ then do+ setEndOfStream strm+ return ""+ else do+ let len = BS.length b0+ case len `compare` siz0 of+ LT -> tryRead (siz0 - len) (b0 :)+ EQ -> return b0+ GT -> do+ let (b1, b2) = BS.splitAt siz0 b0+ writePendingData strm b2+ return b1+ Just b0 -> do+ clearPendingData strm+ let len = BS.length b0+ tryRead (siz0 - len) (b0 :) where tryRead siz build = do mb <- tryTakeRecvStreamQ strm case mb of- Nothing -> return $ BS.concat $ build []- Just b -> do- if b == "" then do- setEndOfStream strm- return $ BS.concat $ build []- else do- let len = BS.length b- case len `compare` siz of- LT -> tryRead (siz - len) (build . (b :))- EQ -> return $ BS.concat $ build [b]- GT -> do- let (b1,b2) = BS.splitAt siz b- writePendingData strm b2- return $ BS.concat $ build [b1]+ Nothing -> return $ BS.concat $ build []+ Just b -> do+ if b == ""+ then do+ setEndOfStream strm+ return $ BS.concat $ build []+ else do+ let len = BS.length b+ case len `compare` siz of+ LT -> tryRead (siz - len) (build . (b :))+ EQ -> return $ BS.concat $ build [b]+ GT -> do+ let (b1, b2) = BS.splitAt siz b+ writePendingData strm b2+ return $ BS.concat $ build [b1] ---------------------------------------------------------------- ----------------------------------------------------------------@@ -93,80 +96,87 @@ putRxStreamData :: Stream -> RxStreamData -> IO FlowCntl putRxStreamData s rx@(RxStreamData _ off len _) = do lim <- getRxMaxStreamData s- if len + off > lim then- return OverLimit- else do- dup <- tryReassemble s rx put putFin- return $ if dup then Duplicated else Reassembled+ if len + off > lim+ then return OverLimit+ else do+ dup <- tryReassemble s rx put putFin+ if dup+ then return Duplicated+ else return Reassembled where put "" = return ()- put d = putRecvStreamQ s d+ put d = do+ addRxStreamData s $ BS.length d+ putRecvStreamQ s d putFin = putRecvStreamQ s "" -- fin of StreamState off fin means see-fin-already. -- return value indicates duplication-tryReassemble :: Stream -> RxStreamData -> (StreamData -> IO ()) -> IO () -> IO Bool-tryReassemble Stream{} (RxStreamData "" _ _ False) _ _ = return True+tryReassemble+ :: Stream -> RxStreamData -> (StreamData -> IO ()) -> IO () -> IO Bool+tryReassemble Stream{} (RxStreamData "" _ _ False) _ _ = return True tryReassemble Stream{..} x@(RxStreamData "" off _ True) _ putFin = do si0@(StreamState off0 fin0) <- readIORef streamStateRx- let si1 = si0 { streamFin = True }- if fin0 then do- -- stdoutLogger "Illegal Fin" -- fixme- return True- else case off `compare` off0 of+ let si1 = si0{streamFin = True}+ if fin0+ then do+ -- stdoutLogger "Illegal Fin" -- fixme+ return True+ else case off `compare` off0 of+ LT -> return True+ EQ -> do+ writeIORef streamStateRx si1+ putFin+ return False+ GT -> do+ writeIORef streamStateRx si1+ atomicModifyIORef'' streamReass (Skew.insert x)+ return False+tryReassemble Stream{..} x@(RxStreamData dat off len False) put putFin = do+ si0@(StreamState off0 _) <- readIORef streamStateRx+ case off `compare` off0 of LT -> return True EQ -> do- writeIORef streamStateRx si1- putFin+ put dat+ loop si0 (off0 + len) return False GT -> do- writeIORef streamStateRx si1 atomicModifyIORef'' streamReass (Skew.insert x) return False-tryReassemble Stream{..} x@(RxStreamData dat off len False) put putFin = do- si0@(StreamState off0 _) <- readIORef streamStateRx- case off `compare` off0 of- LT -> return True- EQ -> do- put dat- loop si0 (off0 + len)- return False- GT -> do- atomicModifyIORef'' streamReass (Skew.insert x)- return False where loop si0 xff = do mrxs <- atomicModifyIORef' streamReass (Skew.deleteMinIf xff) case mrxs of- Nothing -> writeIORef streamStateRx si0 { streamOffset = xff }- Just rxs -> do- mapM_ (put . rxstrmData) rxs- let xff1 = nextOff rxs- if hasFin rxs then do- putFin- else do- loop si0 xff1-+ Nothing -> writeIORef streamStateRx si0{streamOffset = xff}+ Just rxs -> do+ mapM_ (put . rxstrmData) rxs+ let xff1 = nextOff rxs+ if hasFin rxs+ then do+ putFin+ else do+ loop si0 xff1 tryReassemble Stream{..} x@(RxStreamData dat off len True) put putFin = do si0@(StreamState off0 fin0) <- readIORef streamStateRx- let si1 = si0 { streamFin = True }- if fin0 then do- -- stdoutLogger "Illegal Fin" -- fixme- return True- else case off `compare` off0 of- LT -> return True- EQ -> do- let off1 = off0 + len- writeIORef streamStateRx si1 { streamOffset = off1 }- put dat- putFin- return False- GT -> do- writeIORef streamStateRx si1- atomicModifyIORef'' streamReass (Skew.insert x)- return False+ let si1 = si0{streamFin = True}+ if fin0+ then do+ -- stdoutLogger "Illegal Fin" -- fixme+ return True+ else case off `compare` off0 of+ LT -> return True+ EQ -> do+ let off1 = off0 + len+ writeIORef streamStateRx si1{streamOffset = off1}+ put dat+ putFin+ return False+ GT -> do+ writeIORef streamStateRx si1+ atomicModifyIORef'' streamReass (Skew.insert x)+ return False hasFin :: Seq RxStreamData -> Bool hasFin s = case Seq.viewr s of- Seq.EmptyR -> False- _ Seq.:> x -> rxstrmFin x+ Seq.EmptyR -> False+ _ Seq.:> x -> rxstrmFin x
Network/QUIC/Stream/Skew.hs view
@@ -1,12 +1,12 @@ module Network.QUIC.Stream.Skew (- Skew(..)- , empty- , insert- , deleteMin- , deleteMinIf--- , deleteMin'--- , showSkew- ) where+ Skew (..),+ empty,+ insert,+ deleteMin,+ deleteMinIf,+ -- , deleteMin'+ -- , showSkew+) where import Data.Maybe import Data.Sequence (Seq, (><))@@ -17,7 +17,7 @@ ---------------------------------------------------------------- -data Skew a = Leaf | Node (Skew a) (Seq a) (Skew a) deriving Show+data Skew a = Leaf | Node (Skew a) (Seq a) (Skew a) deriving (Show) empty :: Skew a empty = Leaf@@ -32,14 +32,14 @@ -- | Finding the minimum element. Worst-case: O(1). minimum :: Skew a -> Maybe (Seq a)-minimum Leaf = Nothing+minimum Leaf = Nothing minimum (Node _ f _) = Just f ---------------------------------------------------------------- -- | Deleting the minimum element. Worst-case: O(N), amortized: O(log N). deleteMin' :: Frag a => Skew a -> Skew a-deleteMin' Leaf = Leaf+deleteMin' Leaf = Leaf deleteMin' (Node l _ r) = merge l r deleteMin :: Frag a => Skew a -> (Skew a, Maybe (Seq a))@@ -47,11 +47,11 @@ deleteMinIf :: Frag a => Int -> Skew a -> (Skew a, Maybe (Seq a)) deleteMinIf off h = case minimum h of- jf@(Just f)- | currOff f == off -> (deleteMin' h, jf)- | currOff f < off && off < nextOff f -> (deleteMin' h, shrink off <$> jf)- | nextOff f <= off -> (deleteMin' h, Nothing)- _ -> (h, Nothing)+ jf@(Just f)+ | currOff f == off -> (deleteMin' h, jf)+ | currOff f < off && off < nextOff f -> (deleteMin' h, shrink off <$> jf)+ | nextOff f <= off -> (deleteMin' h, Nothing)+ _ -> (h, Nothing) ---------------------------------------------------------------- @@ -60,15 +60,17 @@ merge t1 Leaf = t1 merge Leaf t2 = t2 merge t1@(Node l1 f1 r1) t2@(Node l2 f2 r2)- | e1 < s2 = Node r1 f1 (merge l1 t2)- | e2 < s1 = Node r2 f2 (merge l2 t1)- | otherwise = let f12 | e1 == s2 = f1 >< f2- | s1 == e2 = f2 >< f1- | s1 <= s2 && e2 <= e1 = f1- | s2 <= s1 && e1 <= e2 = f2- | s1 <= s2 = f1 >< shrink e1 f2- | otherwise = f2 >< shrink e2 f1- in Node (merge l1 l2) f12 (merge r1 r2)+ | e1 < s2 = Node r1 f1 (merge l1 t2)+ | e2 < s1 = Node r2 f2 (merge l2 t1)+ | otherwise =+ let f12+ | e1 == s2 = f1 >< f2+ | s1 == e2 = f2 >< f1+ | s1 <= s2 && e2 <= e1 = f1+ | s2 <= s1 && e1 <= e2 = f2+ | s1 <= s2 = f1 >< shrink e1 f2+ | otherwise = f2 >< shrink e2 f1+ in Node (merge l1 l2) f12 (merge r1 r2) where s1 = currOff f1 e1 = nextOff f1
Network/QUIC/Stream/Table.hs view
@@ -1,20 +1,20 @@ {-# LANGUAGE OverloadedStrings #-} module Network.QUIC.Stream.Table (- StreamTable- , emptyStreamTable- , lookupStream- , insertStream- , deleteStream- , insertCryptoStreams- , deleteCryptoStream- , lookupCryptoStream- ) where+ StreamTable,+ emptyStreamTable,+ lookupStream,+ insertStream,+ deleteStream,+ insertCryptoStreams,+ deleteCryptoStream,+ lookupCryptoStream,+) where import Data.IntMap.Strict (IntMap) import qualified Data.IntMap.Strict as Map -import {-# Source #-} Network.QUIC.Connection.Types+import {-# SOURCE #-} Network.QUIC.Connection.Types import Network.QUIC.Stream.Types import Network.QUIC.Types @@ -38,34 +38,35 @@ ---------------------------------------------------------------- -initialCryptoStreamId,handshakeCryptoStreamId,rtt1CryptoStreamId :: StreamId-initialCryptoStreamId = -1+initialCryptoStreamId, handshakeCryptoStreamId, rtt1CryptoStreamId :: StreamId+initialCryptoStreamId = -1 handshakeCryptoStreamId = -2-rtt1CryptoStreamId = -3+rtt1CryptoStreamId = -3 toCryptoStreamId :: EncryptionLevel -> StreamId-toCryptoStreamId InitialLevel = initialCryptoStreamId+toCryptoStreamId InitialLevel = initialCryptoStreamId -- This is to generate an error packet of CRYPTO in 0-RTT-toCryptoStreamId RTT0Level = rtt1CryptoStreamId+toCryptoStreamId RTT0Level = rtt1CryptoStreamId toCryptoStreamId HandshakeLevel = handshakeCryptoStreamId-toCryptoStreamId RTT1Level = rtt1CryptoStreamId+toCryptoStreamId RTT1Level = rtt1CryptoStreamId ---------------------------------------------------------------- insertCryptoStreams :: Connection -> StreamTable -> IO StreamTable insertCryptoStreams conn stbl = do- strm1 <- newStream conn initialCryptoStreamId- strm2 <- newStream conn handshakeCryptoStreamId- strm3 <- newStream conn rtt1CryptoStreamId- return $ insertStream initialCryptoStreamId strm1- $ insertStream handshakeCryptoStreamId strm2- $ insertStream rtt1CryptoStreamId strm3 stbl+ strm1 <- newStream conn initialCryptoStreamId 0 0+ strm2 <- newStream conn handshakeCryptoStreamId 0 0+ strm3 <- newStream conn rtt1CryptoStreamId 0 0+ return $+ insertStream initialCryptoStreamId strm1 $+ insertStream handshakeCryptoStreamId strm2 $+ insertStream rtt1CryptoStreamId strm3 stbl deleteCryptoStream :: EncryptionLevel -> StreamTable -> StreamTable-deleteCryptoStream InitialLevel = deleteStream initialCryptoStreamId-deleteCryptoStream RTT0Level = deleteStream rtt1CryptoStreamId+deleteCryptoStream InitialLevel = deleteStream initialCryptoStreamId+deleteCryptoStream RTT0Level = deleteStream rtt1CryptoStreamId deleteCryptoStream HandshakeLevel = deleteStream handshakeCryptoStreamId-deleteCryptoStream RTT1Level = deleteStream rtt1CryptoStreamId+deleteCryptoStream RTT1Level = deleteStream rtt1CryptoStreamId ----------------------------------------------------------------
Network/QUIC/Stream/Types.hs view
@@ -1,25 +1,23 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.Stream.Types (- Stream(..)- , newStream- , TxStreamData(..)- , Flow(..)- , defaultFlow- , flowWindow- , StreamState(..)- , RecvStreamQ(..)- , RxStreamData(..)- , Length- , syncFinTx- , waitFinTx- ) where+ Stream (..),+ newStream,+ TxStreamData (..),+ StreamState (..),+ RecvStreamQ (..),+ RxStreamData (..),+ Length,+ syncFinTx,+ waitFinTx,+) where import qualified Data.ByteString as BS+import Network.Control import UnliftIO.Concurrent import UnliftIO.STM -import {-# Source #-} Network.QUIC.Connection.Types+import {-# SOURCE #-} Network.QUIC.Connection.Types import Network.QUIC.Imports import Network.QUIC.Stream.Frag import Network.QUIC.Stream.Skew@@ -29,31 +27,34 @@ ---------------------------------------------------------------- -- | An abstract data type for streams.-data Stream = Stream {- streamId :: StreamId -- ^ Getting stream identifier.- , streamConnection :: Connection- -- "counter" is equivalent to "offset".- -- It is duplicated but used for API level flow control.- , streamFlowTx :: TVar Flow -- counter, maxDax- , streamFlowRx :: IORef Flow -- counter, maxDax- , streamStateTx :: IORef StreamState -- offset, fin- , streamStateRx :: IORef StreamState -- offset, fin- , streamRecvQ :: RecvStreamQ -- input bytestring- , streamReass :: IORef (Skew RxStreamData) -- input stream fragments to streamQ- , streamSyncFinTx :: MVar ()- }+data Stream = Stream+ { streamId :: StreamId+ -- ^ Getting stream identifier.+ , streamConnection :: Connection+ , -- "counter" is equivalent to "offset".+ -- It is duplicated but used for API level flow control.+ streamFlowTx :: TVar TxFlow -- counter, maxDax+ , streamFlowRx :: IORef RxFlow -- counter, maxDax+ , streamStateTx :: IORef StreamState -- offset, fin+ , streamStateRx :: IORef StreamState -- offset, fin+ , streamRecvQ :: RecvStreamQ -- input bytestring+ , streamReass :: IORef (Skew RxStreamData) -- input stream fragments to streamQ+ , streamSyncFinTx :: MVar ()+ } instance Show Stream where show s = show $ streamId s -newStream :: Connection -> StreamId -> IO Stream-newStream conn sid = Stream sid conn <$> newTVarIO defaultFlow- <*> newIORef defaultFlow- <*> newIORef emptyStreamState- <*> newIORef emptyStreamState- <*> newRecvStreamQ- <*> newIORef Skew.empty- <*> newEmptyMVar+newStream :: Connection -> Int -> Int -> StreamId -> IO Stream+newStream conn sid txLim rxLim =+ Stream sid conn+ <$> newTVarIO (newTxFlow txLim)+ <*> newIORef (newRxFlow rxLim)+ <*> newIORef emptyStreamState+ <*> newIORef emptyStreamState+ <*> newRecvStreamQ+ <*> newIORef Skew.empty+ <*> newEmptyMVar syncFinTx :: Stream -> IO () syncFinTx s = void $ tryPutMVar (streamSyncFinTx s) ()@@ -67,52 +68,41 @@ data TxStreamData = TxStreamData Stream [StreamData] Length Fin -data RxStreamData = RxStreamData {- rxstrmData :: StreamData- , rxstrmOff :: Offset- , rxstrmLen :: Length- , rxstrmFin :: Fin- } deriving (Eq, Show)+data RxStreamData = RxStreamData+ { rxstrmData :: StreamData+ , rxstrmOff :: Offset+ , rxstrmLen :: Length+ , rxstrmFin :: Fin+ }+ deriving (Eq, Show) instance Frag RxStreamData where currOff r = rxstrmOff r nextOff r = rxstrmOff r + rxstrmLen r- shrink off' (RxStreamData bs off len fin) =+ shrink off' (RxStreamData bs off len fin) = let n = off' - off- bs' = BS.drop n bs+ bs' = BS.drop n bs len' = len - n- in RxStreamData bs' off' len' fin--------------------------------------------------------------------data Flow = Flow {- flowData :: Int- , flowMaxData :: Int- } deriving (Eq, Show)--defaultFlow :: Flow-defaultFlow = Flow 0 0--flowWindow :: Flow -> Int-flowWindow Flow{..} = flowMaxData - flowData+ in RxStreamData bs' off' len' fin ---------------------------------------------------------------- -data StreamState = StreamState {- streamOffset :: Offset- , streamFin :: Fin- } deriving (Eq, Show)+data StreamState = StreamState+ { streamOffset :: Offset+ , streamFin :: Fin+ }+ deriving (Eq, Show) emptyStreamState :: StreamState emptyStreamState = StreamState 0 False ---------------------------------------------------------------- -data RecvStreamQ = RecvStreamQ {- recvStreamQ :: TQueue ByteString- , pendingData :: IORef (Maybe ByteString)- , endOfStream :: IORef Bool- }+data RecvStreamQ = RecvStreamQ+ { recvStreamQ :: TQueue ByteString+ , pendingData :: IORef (Maybe ByteString)+ , endOfStream :: IORef Bool+ } newRecvStreamQ :: IO RecvStreamQ newRecvStreamQ = RecvStreamQ <$> newTQueueIO <*> newIORef Nothing <*> newIORef False
Network/QUIC/TLS.hs view
@@ -2,9 +2,9 @@ {-# LANGUAGE RecordWildCards #-} module Network.QUIC.TLS (- clientHandshaker- , serverHandshaker- ) where+ clientHandshaker,+ serverHandshaker,+) where import Data.Default.Class import Network.TLS hiding (Version)@@ -16,52 +16,59 @@ import Network.QUIC.Types sessionManager :: SessionEstablish -> SessionManager-sessionManager establish = SessionManager {- sessionEstablish = establish- , sessionResume = \_ -> return Nothing- , sessionResumeOnlyOnce = \_ -> return Nothing- , sessionInvalidate = \_ -> return ()- }+sessionManager establish =+ SessionManager+ { sessionEstablish = establish+ , sessionResume = \_ -> return Nothing+ , sessionResumeOnlyOnce = \_ -> return Nothing+ , sessionInvalidate = \_ -> return ()+ } -clientHandshaker :: QUICCallbacks- -> ClientConfig- -> Version- -> AuthCIDs- -> SessionEstablish- -> Bool- -> IO ()+clientHandshaker+ :: QUICCallbacks+ -> ClientConfig+ -> Version+ -> AuthCIDs+ -> SessionEstablish+ -> Bool+ -> IO () clientHandshaker callbacks ClientConfig{..} ver myAuthCIDs establish use0RTT = do caStore <- if ccValidate then getSystemCertificateStore else return mempty tlsQUICClient (cparams caStore) callbacks where- cparams caStore = (defaultParamsClient ccServerName "") {- clientShared = cshared caStore- , clientHooks = hook- , clientSupported = supported- , clientDebug = debug- , clientWantSessionResume = resumptionSession ccResumption- , clientEarlyData = if use0RTT then Just "" else Nothing- }+ cparams caStore =+ (defaultParamsClient ccServerName "")+ { clientShared = cshared caStore+ , clientHooks = hook+ , clientSupported = supported+ , clientDebug = debug+ , clientWantSessionResume = resumptionSession ccResumption+ , clientEarlyData = if use0RTT then Just "" else Nothing+ } convTP = onTransportParametersCreated ccHooks params = convTP $ setCIDsToParameters myAuthCIDs ccParameters convExt = onTLSExtensionCreated ccHooks skipValidation = ValidationCache (\_ _ _ -> return ValidationCachePass) (\_ _ _ -> return ())- cshared caStore = def {- sharedValidationCache = if ccValidate then def else skipValidation- , sharedCAStore = caStore- , sharedHelloExtensions = convExt $ parametersToExtensionRaw ver params- , sharedSessionManager = sessionManager establish- }- hook = def {- onSuggestALPN = ccALPN ver- }- supported = defaultSupported {- supportedCiphers = ccCiphers- , supportedGroups = ccGroups- }- debug = def {- debugKeyLogger = ccKeyLog- }+ cshared caStore =+ def+ { sharedValidationCache = if ccValidate then def else skipValidation+ , sharedCAStore = caStore+ , sharedHelloExtensions = convExt $ parametersToExtensionRaw ver params+ , sharedSessionManager = sessionManager establish+ }+ hook =+ def+ { onSuggestALPN = ccALPN ver+ }+ supported =+ defaultSupported+ { supportedCiphers = ccCiphers+ , supportedGroups = ccGroups+ }+ debug =+ def+ { debugKeyLogger = ccKeyLog+ } parametersToExtensionRaw :: Version -> Parameters -> [ExtensionRaw] parametersToExtensionRaw ver params = [ExtensionRaw tpId eParams]@@ -69,41 +76,47 @@ tpId = extensionIDForTtransportParameter ver eParams = encodeParameters params -serverHandshaker :: QUICCallbacks- -> ServerConfig- -> Version- -> IO Parameters- -> IO ()+serverHandshaker+ :: QUICCallbacks+ -> ServerConfig+ -> Version+ -> IO Parameters+ -> IO () serverHandshaker callbacks ServerConfig{..} ver getParams = tlsQUICServer sparams callbacks where- sparams = def {- serverShared = sshared- , serverHooks = hook- , serverSupported = supported- , serverDebug = debug- , serverEarlyDataSize = if scUse0RTT then quicMaxEarlyDataSize else 0- }+ sparams =+ def+ { serverShared = sshared+ , serverHooks = hook+ , serverSupported = supported+ , serverDebug = debug+ , serverEarlyDataSize = if scUse0RTT then quicMaxEarlyDataSize else 0+ } convTP = onTransportParametersCreated scHooks convExt = onTLSExtensionCreated scHooks- sshared = def {- sharedCredentials = scCredentials- , sharedSessionManager = scSessionManager- }- hook = def {- onALPNClientSuggest = case scALPN of- Nothing -> Nothing- Just io -> Just $ io ver- , onEncryptedExtensionsCreating = \exts0 -> do- params <- getParams- let exts = convExt $ parametersToExtensionRaw ver $ convTP params- return $ exts ++ exts0- }- supported = def {- supportedVersions = [TLS13]- , supportedCiphers = scCiphers- , supportedGroups = scGroups- }- debug = def {- debugKeyLogger = scKeyLog- }+ sshared =+ def+ { sharedCredentials = scCredentials+ , sharedSessionManager = scSessionManager+ }+ hook =+ def+ { onALPNClientSuggest = case scALPN of+ Nothing -> Nothing+ Just io -> Just $ io ver+ , onEncryptedExtensionsCreating = \exts0 -> do+ params <- getParams+ let exts = convExt $ parametersToExtensionRaw ver $ convTP params+ return $ exts ++ exts0+ }+ supported =+ def+ { supportedVersions = [TLS13]+ , supportedCiphers = scCiphers+ , supportedGroups = scGroups+ }+ debug =+ def+ { debugKeyLogger = scKeyLog+ }
Network/QUIC/Types.hs view
@@ -1,20 +1,20 @@ module Network.QUIC.Types (- Bytes- , Close- , Direction(..)- , SizedBuffer(..)- , module Network.QUIC.Types.Ack- , module Network.QUIC.Types.CID- , module Network.QUIC.Types.Constants- , module Network.QUIC.Types.Error- , module Network.QUIC.Types.Exception- , module Network.QUIC.Types.Frame- , module Network.QUIC.Types.Integer- , module Network.QUIC.Types.Packet- , module Network.QUIC.Types.Queue- , module Network.QUIC.Types.Resumption- , module Network.QUIC.Types.Time- ) where+ Bytes,+ Close,+ Direction (..),+ SizedBuffer (..),+ module Network.QUIC.Types.Ack,+ module Network.QUIC.Types.CID,+ module Network.QUIC.Types.Constants,+ module Network.QUIC.Types.Error,+ module Network.QUIC.Types.Exception,+ module Network.QUIC.Types.Frame,+ module Network.QUIC.Types.Integer,+ module Network.QUIC.Types.Packet,+ module Network.QUIC.Types.Queue,+ module Network.QUIC.Types.Resumption,+ module Network.QUIC.Types.Time,+) where import Network.QUIC.Imports import Network.QUIC.Types.Ack
Network/QUIC/Types/Ack.hs view
@@ -6,10 +6,10 @@ type PacketNumber = Int type Range = Int-type Gap = Int+type Gap = Int -data AckInfo = AckInfo PacketNumber Range [(Gap,Range)]- deriving (Eq, Show)+data AckInfo = AckInfo PacketNumber Range [(Gap, Range)]+ deriving (Eq, Show) ackInfo0 :: AckInfo ackInfo0 = AckInfo (-1) 0 []@@ -24,18 +24,18 @@ -- >>> toAckInfo [9,8,7,5,4] -- AckInfo 9 2 [(0,1)] toAckInfo :: [PacketNumber] -> AckInfo-toAckInfo [] = error "toAckInfo"+toAckInfo [] = error "toAckInfo" toAckInfo [l] = AckInfo l 0 []-toAckInfo (l:ls) = ack l ls 0+toAckInfo (l : ls) = ack l ls 0 where- ack _ [] fr = AckInfo l fr []- ack p (x:xs) fr- | p - 1 == x = ack x xs (fr+1)- | otherwise = AckInfo l fr $ ranges x xs (fromIntegral (p - x) - 2) 0+ ack _ [] fr = AckInfo l fr []+ ack p (x : xs) fr+ | p - 1 == x = ack x xs (fr + 1)+ | otherwise = AckInfo l fr $ ranges x xs (fromIntegral (p - x) - 2) 0 ranges _ [] g r = [(g, r)]- ranges p (x:xs) g r- | p - 1 == x = ranges x xs g (r+1)- | otherwise = (g, r) : ranges x xs (fromIntegral (p - x) - 2) 0+ ranges p (x : xs) g r+ | p - 1 == x = ranges x xs g (r + 1)+ | otherwise = (g, r) : ranges x xs (fromIntegral (p - x) - 2) 0 -- | -- >>> fromAckInfo $ AckInfo 9 0 []@@ -50,9 +50,9 @@ fromAckInfo (AckInfo lpn fr grs) = loop grs [stt .. lpn] where stt = lpn - fromIntegral fr- loop _ [] = error "loop"- loop [] acc = acc- loop ((g,r):xs) acc@(s:_) = loop xs ([z - fromIntegral r .. z] ++ acc)+ loop _ [] = error "loop"+ loop [] acc = acc+ loop ((g, r) : xs) acc@(s : _) = loop xs ([z - fromIntegral r .. z] ++ acc) where z = s - fromIntegral g - 2 @@ -67,36 +67,35 @@ -- [8,9] fromAckInfoWithMin :: AckInfo -> PacketNumber -> [PacketNumber] fromAckInfoWithMin (AckInfo lpn fr grs) lim- | stt < lim = [lim .. lpn]- | otherwise = loop grs [stt .. lpn]+ | stt < lim = [lim .. lpn]+ | otherwise = loop grs [stt .. lpn] where stt = lpn - fromIntegral fr- loop _ [] = error "loop"- loop [] acc = acc- loop ((g,r):xs) acc@(s:_)- | z < lim = acc- |otherwise = loop xs ([r' .. z] ++ acc)+ loop _ [] = error "loop"+ loop [] acc = acc+ loop ((g, r) : xs) acc@(s : _)+ | z < lim = acc+ | otherwise = loop xs ([r' .. z] ++ acc) where z = s - fromIntegral g - 2 r' = max lim (z - fromIntegral r) fromAckInfoToPred :: AckInfo -> (PacketNumber -> Bool) fromAckInfoToPred (AckInfo lpn fr grs) =- \x -> any (f x) $ loop grs [(stt,lpn)]+ \x -> any (f x) $ loop grs [(stt, lpn)] where- f x (l,u) = l <= x && x <= u+ f x (l, u) = l <= x && x <= u stt = lpn - fromIntegral fr- loop _ [] = error "loop"- loop [] acc = acc- loop ((g,r):xs) acc@((s,_):_) = loop xs $ (z - fromIntegral r, z) : acc+ loop _ [] = error "loop"+ loop [] acc = acc+ loop ((g, r) : xs) acc@((s, _) : _) = loop xs $ (z - fromIntegral r, z) : acc where z = s - fromIntegral g - 2 ---------------------------------------------------------------- newtype PeerPacketNumbers = PeerPacketNumbers IntSet- deriving (Eq, Show)+ deriving (Eq, Show) emptyPeerPacketNumbers :: PeerPacketNumbers emptyPeerPacketNumbers = PeerPacketNumbers IntSet.empty-
Network/QUIC/Types/CID.hs view
@@ -1,17 +1,17 @@ module Network.QUIC.Types.CID (- CID(..)- , myCIDLength- , newCID- , fromCID- , toCID- , makeCID- , unpackCID- , StatelessResetToken(..)- , newStatelessResetToken- , PathData(..)- , newPathData- , CIDInfo(..)- ) where+ CID (..),+ myCIDLength,+ newCID,+ fromCID,+ toCID,+ makeCID,+ unpackCID,+ StatelessResetToken (..),+ newStatelessResetToken,+ PathData (..),+ newPathData,+ CIDInfo (..),+) where import qualified Data.ByteString.Short as Short @@ -45,19 +45,20 @@ len = fromIntegral $ Short.length sbs -- 16 bytes-newtype StatelessResetToken = StatelessResetToken Bytes deriving (Eq,Ord,Show)+newtype StatelessResetToken = StatelessResetToken Bytes deriving (Eq, Ord, Show) newStatelessResetToken :: IO StatelessResetToken newStatelessResetToken = StatelessResetToken <$> getRandomBytes 16 -- 8 bytes-newtype PathData = PathData Bytes deriving (Eq,Show)+newtype PathData = PathData Bytes deriving (Eq, Show) newPathData :: IO PathData newPathData = PathData <$> getRandomBytes 8 -data CIDInfo = CIDInfo {- cidInfoSeq :: Int- , cidInfoCID :: CID- , cidInfoSRT :: StatelessResetToken- } deriving (Eq, Ord, Show)+data CIDInfo = CIDInfo+ { cidInfoSeq :: Int+ , cidInfoCID :: CID+ , cidInfoSRT :: StatelessResetToken+ }+ deriving (Eq, Ord, Show)
Network/QUIC/Types/Error.hs view
@@ -9,6 +9,7 @@ -- | Transport errors of QUIC. newtype TransportError = TransportError Int deriving (Eq) +{- FOURMOLU_DISABLE -} pattern NoError :: TransportError pattern NoError = TransportError 0x0 @@ -87,6 +88,7 @@ Just e -> "TLS " ++ show e Nothing -> "TLS Alert " ++ show x | otherwise = "TransportError " ++ printf "%x" x+{- FOURMOLU_ENABLE -} -- | Converting a TLS alert to a corresponding transport error. cryptoError :: TLS.AlertDescription -> TransportError@@ -95,4 +97,5 @@ ec = 0x100 + fromIntegral (fromAlertDescription ad) -- | Application protocol errors of QUIC.-newtype ApplicationProtocolError = ApplicationProtocolError Int deriving (Eq, Show)+newtype ApplicationProtocolError = ApplicationProtocolError Int+ deriving (Eq, Show)
Network/QUIC/Types/Exception.hs view
@@ -9,30 +9,31 @@ import Network.QUIC.Types.Packet -- | User level exceptions for QUIC.-data QUICException =- ConnectionIsClosed -- NoError- | TransportErrorIsReceived TransportError ReasonPhrase- | TransportErrorIsSent TransportError ReasonPhrase- | ApplicationProtocolErrorIsReceived ApplicationProtocolError ReasonPhrase- | ApplicationProtocolErrorIsSent ApplicationProtocolError ReasonPhrase- | ConnectionIsTimeout String- | ConnectionIsReset- | StreamIsClosed- | HandshakeFailed TLS.AlertDescription -- failed in my side- | VersionIsUnknown Word32- | NoVersionIsSpecified- | VersionNegotiationFailed- | BadThingHappen E.SomeException- deriving (Show)+data QUICException+ = ConnectionIsClosed -- NoError+ | TransportErrorIsReceived TransportError ReasonPhrase+ | TransportErrorIsSent TransportError ReasonPhrase+ | ApplicationProtocolErrorIsReceived ApplicationProtocolError ReasonPhrase+ | ApplicationProtocolErrorIsSent ApplicationProtocolError ReasonPhrase+ | ConnectionIsTimeout String+ | ConnectionIsReset+ | StreamIsClosed+ | HandshakeFailed TLS.AlertDescription -- failed in my side+ | VersionIsUnknown Word32+ | NoVersionIsSpecified+ | VersionNegotiationFailed+ | BadThingHappen E.SomeException+ deriving (Show) instance E.Exception QUICException -data InternalControl = MustNotReached- | ExitConnection- | WrongTransportParameter- | WrongVersionInformation- | BreakForever- deriving (Eq, Show)+data InternalControl+ = MustNotReached+ | ExitConnection+ | WrongTransportParameter+ | WrongVersionInformation+ | BreakForever+ deriving (Eq, Show) instance E.Exception InternalControl @@ -40,10 +41,11 @@ instance E.Exception NextVersion -data Abort = Abort ApplicationProtocolError ReasonPhrase- | VerNego VersionInfo- deriving (Show)+data Abort+ = Abort ApplicationProtocolError ReasonPhrase+ | VerNego VersionInfo+ deriving (Show) instance E.Exception Abort where- fromException = E.asyncExceptionFromException- toException = E.asyncExceptionToException+ fromException = E.asyncExceptionFromException+ toException = E.asyncExceptionToException
Network/QUIC/Types/Frame.hs view
@@ -18,29 +18,30 @@ type ReasonPhrase = ShortByteString type SeqNum = Int -data Frame = Padding Int- | Ping- | Ack AckInfo Delay- | ResetStream StreamId ApplicationProtocolError Int- | StopSending StreamId ApplicationProtocolError- | CryptoF Offset CryptoData- | NewToken Token- | StreamF StreamId Offset [StreamData] Fin- | MaxData Int- | MaxStreamData StreamId Int- | MaxStreams Direction Int- | DataBlocked Int- | StreamDataBlocked StreamId Int- | StreamsBlocked Direction Int- | NewConnectionID CIDInfo SeqNum -- retire prior to- | RetireConnectionID SeqNum- | PathChallenge PathData- | PathResponse PathData- | ConnectionClose TransportError FrameType ReasonPhrase- | ConnectionCloseApp ApplicationProtocolError ReasonPhrase- | HandshakeDone- | UnknownFrame Int- deriving (Eq,Show)+data Frame+ = Padding Int+ | Ping+ | Ack AckInfo Delay+ | ResetStream StreamId ApplicationProtocolError Int+ | StopSending StreamId ApplicationProtocolError+ | CryptoF Offset CryptoData+ | NewToken Token+ | StreamF StreamId Offset [StreamData] Fin+ | MaxData Int+ | MaxStreamData StreamId Int+ | MaxStreams Direction Int+ | DataBlocked Int+ | StreamDataBlocked StreamId Int+ | StreamsBlocked Direction Int+ | NewConnectionID CIDInfo SeqNum -- retire prior to+ | RetireConnectionID SeqNum+ | PathChallenge PathData+ | PathResponse PathData+ | ConnectionClose TransportError FrameType ReasonPhrase+ | ConnectionCloseApp ApplicationProtocolError ReasonPhrase+ | HandshakeDone+ | UnknownFrame Int+ deriving (Eq, Show) -- | Stream identifier. -- This should be 62-bit interger.@@ -49,11 +50,11 @@ -- | Checking if a stream is client-initiated bidirectional. isClientInitiatedBidirectional :: StreamId -> Bool-isClientInitiatedBidirectional sid = (0b11 .&. sid) == 0+isClientInitiatedBidirectional sid = (0b11 .&. sid) == 0 -- | Checking if a stream is server-initiated bidirectional. isServerInitiatedBidirectional :: StreamId -> Bool-isServerInitiatedBidirectional sid = (0b11 .&. sid) == 1+isServerInitiatedBidirectional sid = (0b11 .&. sid) == 1 -- | Checking if a stream is client-initiated unidirectional. isClientInitiatedUnidirectional :: StreamId -> Bool@@ -86,6 +87,7 @@ emptyToken :: Token emptyToken = "" +{- FOURMOLU_DISABLE -} ackEliciting :: Frame -> Bool ackEliciting Padding{} = False ackEliciting Ack{} = False@@ -103,3 +105,9 @@ inFlight ConnectionClose{} = False inFlight ConnectionCloseApp{} = False inFlight _ = True++rateControled :: Frame -> Bool+rateControled ResetStream{} = True+rateControled StopSending{} = True+rateControled _ = False+{- FOURMOLU_ENABLE -}
Network/QUIC/Types/Integer.hs view
@@ -1,14 +1,14 @@ {-# LANGUAGE BinaryLiterals #-} module Network.QUIC.Types.Integer (- encodeInt- , encodeInt8- , encodeInt'- , encodeInt'2- , encodeInt'4- , decodeInt- , decodeInt'- ) where+ encodeInt,+ encodeInt8,+ encodeInt',+ encodeInt'2,+ encodeInt'4,+ decodeInt,+ decodeInt',+) where import Data.ByteString.Internal (unsafeCreate) import Foreign.Ptr@@ -32,13 +32,13 @@ -- "7bbd" -- >>> enc16 $ encodeInt 37 -- "25"-encodeInt :: Int64 -> ByteString+encodeInt :: Int64 -> ByteString encodeInt i = unsafeCreate n $ go tag n i' where- (tag,n,i') = tagLen i+ (tag, n, i') = tagLen i {-# NOINLINE encodeInt #-} -encodeInt8 :: Int64 -> ByteString+encodeInt8 :: Int64 -> ByteString encodeInt8 i = unsafeCreate n $ go tag n i where n = 8@@ -48,7 +48,7 @@ encodeInt' :: WriteBuffer -> Int64 -> IO () encodeInt' wbuf i = go' tag n i' wbuf where- (tag,n,i') = tagLen i+ (tag, n, i') = tagLen i encodeInt'2 :: WriteBuffer -> Int64 -> IO () encodeInt'2 wbuf i = go' tag n i' wbuf@@ -65,10 +65,11 @@ i' = i !<<. 32 tagLen :: Int64 -> (Word8, Int, Int64)-tagLen i | i <= 63 = (0b00000000, 1, i !<<. 56)- | i <= 16383 = (0b01000000, 2, i !<<. 48)- | i <= 1073741823 = (0b10000000, 4, i !<<. 32)- | otherwise = (0b11000000, 8, i)+tagLen i+ | i <= 63 = (0b00000000, 1, i !<<. 56)+ | i <= 16383 = (0b01000000, 2, i !<<. 48)+ | i <= 1073741823 = (0b10000000, 4, i !<<. 32)+ | otherwise = (0b11000000, 8, i) {-# INLINE tagLen #-} msb8 :: Int64 -> Word8@@ -128,13 +129,13 @@ let flag = b0 !>>. 6 b1 = fromIntegral (b0 .&. 0b00111111) case flag of- 0 -> return b1- 1 -> loop b1 1- 2 -> loop b1 3- _ -> loop b1 7+ 0 -> return b1+ 1 -> loop b1 1+ 2 -> loop b1 3+ _ -> loop b1 7 where loop :: Int64 -> Int -> IO Int64 loop r 0 = return r loop r n = do b <- fromIntegral <$> read8 rbuf- loop (r*256 + b) (n - 1)+ loop (r * 256 + b) (n - 1)
Network/QUIC/Types/Packet.hs view
@@ -4,8 +4,8 @@ module Network.QUIC.Types.Packet where import Data.Ix+import Network.TLS.QUIC (ExtensionID, extensionID_QuicTransportParameters) import Network.UDP-import Network.TLS.QUIC (extensionID_QuicTransportParameters, ExtensionID) import Text.Printf import Network.QUIC.Imports@@ -19,6 +19,7 @@ -- | QUIC version. newtype Version = Version Word32 deriving (Eq, Ord) +{- FOURMOLU_DISABLE -} pattern Negotiation :: Version pattern Negotiation = Version 0 pattern Version1 :: Version@@ -40,16 +41,18 @@ show ver@(Version v) | isGreasingVersion ver = "Greasing 0x" ++ printf "%08x" v | otherwise = "Version 0x" ++ printf "%08x" v+{- FOURMOLU_ENABLE -} isGreasingVersion :: Version -> Bool isGreasingVersion (Version v) = v .&. 0x0a0a0a0a == 0x0a0a0a0a ---------------------------------------------------------------- -data VersionInfo = VersionInfo {- chosenVersion :: Version- , otherVersions :: [Version]- } deriving (Eq, Show)+data VersionInfo = VersionInfo+ { chosenVersion :: Version+ , otherVersions :: [Version]+ }+ deriving (Eq, Show) brokenVersionInfo :: VersionInfo brokenVersionInfo = VersionInfo Negotiation []@@ -59,36 +62,41 @@ extensionIDForTtransportParameter :: Version -> ExtensionID extensionIDForTtransportParameter Version1 = extensionID_QuicTransportParameters extensionIDForTtransportParameter Version2 = extensionID_QuicTransportParameters-extensionIDForTtransportParameter _ = 0xffa5+extensionIDForTtransportParameter _ = 0xffa5 ---------------------------------------------------------------- -data PacketI = PacketIV VersionNegotiationPacket- | PacketIR RetryPacket- | PacketIC CryptPacket EncryptionLevel Int- | PacketIB BrokenPacket- deriving (Eq, Show)+data PacketI+ = PacketIV VersionNegotiationPacket+ | PacketIR RetryPacket+ | PacketIC CryptPacket EncryptionLevel Int+ | PacketIB BrokenPacket+ deriving (Eq, Show) -- Not used internally. Only for 'encodePacket'.-data PacketO = PacketOV VersionNegotiationPacket- | PacketOR RetryPacket- | PacketOP PlainPacket- deriving (Eq, Show)+data PacketO+ = PacketOV VersionNegotiationPacket+ | PacketOR RetryPacket+ | PacketOP PlainPacket+ deriving (Eq, Show) data VersionNegotiationPacket = VersionNegotiationPacket CID CID [Version]- deriving (Eq, Show)+ deriving (Eq, Show) -data RetryPacket = RetryPacket Version CID CID Token (Either CID (ByteString,ByteString))- deriving (Eq, Show)+data RetryPacket+ = RetryPacket Version CID CID Token (Either CID (ByteString, ByteString))+ deriving (Eq, Show) data BrokenPacket = BrokenPacket deriving (Eq, Show) -data Header = Initial Version CID CID Token- | RTT0 Version CID CID- | Handshake Version CID CID- | Short CID- deriving (Eq, Show)+data Header+ = Initial Version CID CID Token+ | RTT0 Version CID CID+ | Handshake Version CID CID+ | Short CID+ deriving (Eq, Show) +{- FOURMOLU_DISABLE -} headerMyCID :: Header -> CID headerMyCID (Initial _ cid _ _) = cid headerMyCID (RTT0 _ cid _) = cid@@ -100,16 +108,18 @@ headerPeerCID (RTT0 _ _ cid) = cid headerPeerCID (Handshake _ _ cid) = cid headerPeerCID Short{} = CID ""+{- FOURMOLU_ENABLE -} data PlainPacket = PlainPacket Header Plain deriving (Eq, Show) data CryptPacket = CryptPacket Header Crypt deriving (Eq, Show) -data Plain = Plain {- plainFlags :: Flags Raw- , plainPacketNumber :: PacketNumber- , plainFrames :: [Frame]- , plainMarks :: Int- } deriving (Eq, Show)+data Plain = Plain+ { plainFlags :: Flags Raw+ , plainPacketNumber :: PacketNumber+ , plainFrames :: [Frame]+ , plainMarks :: Int+ }+ deriving (Eq, Show) defaultPlainMarks :: Int defaultPlainMarks = 0@@ -144,57 +154,66 @@ is4bytesPN :: Int -> Bool is4bytesPN = (`testBit` 9) -data MigrationInfo = MigrationInfo ListenSocket ClientSockAddr CID deriving (Eq, Show)+data MigrationInfo = MigrationInfo ListenSocket ClientSockAddr CID+ deriving (Eq, Show) -data Crypt = Crypt {- cryptPktNumOffset :: Int- , cryptPacket :: ByteString- , cryptMarks :: Int- , cryptMigraionInfo :: Maybe MigrationInfo- } deriving (Eq, Show)+data Crypt = Crypt+ { cryptPktNumOffset :: Int+ , cryptPacket :: ByteString+ , cryptMarks :: Int+ , cryptMigraionInfo :: Maybe MigrationInfo+ }+ deriving (Eq, Show) isCryptDelayed :: Crypt -> Bool isCryptDelayed crypt = cryptMarks crypt `testBit` 1 setCryptDelayed :: Crypt -> Crypt-setCryptDelayed crypt = crypt { cryptMarks = cryptMarks crypt `setBit` 1 }+setCryptDelayed crypt = crypt{cryptMarks = cryptMarks crypt `setBit` 1} data StatelessReset = StatelessReset deriving (Eq, Show) -data ReceivedPacket = ReceivedPacket {- rpCryptPacket :: CryptPacket- , rpTimeRecevied :: TimeMicrosecond- , rpReceivedBytes :: Int- , rpEncryptionLevel :: EncryptionLevel- } deriving (Eq, Show)+data ReceivedPacket = ReceivedPacket+ { rpCryptPacket :: CryptPacket+ , rpTimeRecevied :: TimeMicrosecond+ , rpReceivedBytes :: Int+ , rpEncryptionLevel :: EncryptionLevel+ }+ deriving (Eq, Show) -mkReceivedPacket :: CryptPacket -> TimeMicrosecond -> Int -> EncryptionLevel -> ReceivedPacket-mkReceivedPacket cpkt tim bytes lvl = ReceivedPacket {- rpCryptPacket = cpkt- , rpTimeRecevied = tim- , rpReceivedBytes = bytes- , rpEncryptionLevel = lvl- }+mkReceivedPacket+ :: CryptPacket -> TimeMicrosecond -> Int -> EncryptionLevel -> ReceivedPacket+mkReceivedPacket cpkt tim bytes lvl =+ ReceivedPacket+ { rpCryptPacket = cpkt+ , rpTimeRecevied = tim+ , rpReceivedBytes = bytes+ , rpEncryptionLevel = lvl+ } ---------------------------------------------------------------- -data LongHeaderPacketType = InitialPacketType- | RTT0PacketType- | HandshakePacketType- | RetryPacketType- deriving (Eq, Show)+data LongHeaderPacketType+ = InitialPacketType+ | RTT0PacketType+ | HandshakePacketType+ | RetryPacketType+ deriving (Eq, Show) -data EncryptionLevel = InitialLevel- | RTT0Level- | HandshakeLevel- | RTT1Level- deriving (Eq, Ord, Ix, Show)+data EncryptionLevel+ = InitialLevel+ | RTT0Level+ | HandshakeLevel+ | RTT1Level+ deriving (Eq, Ord, Ix, Show) +{- FOURMOLU_DISABLE -} packetEncryptionLevel :: Header -> EncryptionLevel packetEncryptionLevel Initial{} = InitialLevel packetEncryptionLevel RTT0{} = RTT0Level packetEncryptionLevel Handshake{} = HandshakeLevel packetEncryptionLevel Short{} = RTT1Level+{- FOURMOLU_ENABLE -} ----------------------------------------------------------------
Network/QUIC/Types/Resumption.hs view
@@ -12,20 +12,22 @@ type SessionEstablish = SessionID -> SessionData -> IO () -- | Information about resumption-data ResumptionInfo = ResumptionInfo {- resumptionVersion :: Version- , resumptionSession :: Maybe (SessionID, SessionData)- , resumptionToken :: Token- , resumptionRetry :: Bool- } deriving (Eq, Show)+data ResumptionInfo = ResumptionInfo+ { resumptionVersion :: Version+ , resumptionSession :: Maybe (SessionID, SessionData)+ , resumptionToken :: Token+ , resumptionRetry :: Bool+ }+ deriving (Eq, Show) defaultResumptionInfo :: ResumptionInfo-defaultResumptionInfo = ResumptionInfo {- resumptionVersion = Version1- , resumptionSession = Nothing- , resumptionToken = emptyToken- , resumptionRetry = False- }+defaultResumptionInfo =+ ResumptionInfo+ { resumptionVersion = Version1+ , resumptionSession = Nothing+ , resumptionToken = emptyToken+ , resumptionRetry = False+ } -- | Is 0RTT possible? is0RTTPossible :: ResumptionInfo -> Bool@@ -33,8 +35,8 @@ rtt0OK && (not resumptionRetry || resumptionToken /= emptyToken) where rtt0OK = case resumptionSession of- Nothing -> False- Just (_, sd) -> sessionMaxEarlyDataSize sd == quicMaxEarlyDataSize+ Nothing -> False+ Just (_, sd) -> sessionMaxEarlyDataSize sd == quicMaxEarlyDataSize -- | Is resumption possible? isResumptionPossible :: ResumptionInfo -> Bool@@ -42,5 +44,5 @@ get0RTTCipher :: ResumptionInfo -> Maybe CipherID get0RTTCipher ri = case resumptionSession ri of- Nothing -> Nothing- Just (_, sd) -> Just $ sessionCipher sd+ Nothing -> Nothing+ Just (_, sd) -> Just $ sessionCipher sd
Network/QUIC/Types/Time.hs view
@@ -1,23 +1,23 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Network.QUIC.Types.Time (- Milliseconds(..)- , Microseconds(..)- , milliToMicro- , microToMilli- , TimeMicrosecond- , timeMicrosecond0- , getTimeMicrosecond- , getElapsedTimeMicrosecond- , elapsedTimeMicrosecond- , getTimeoutInMicrosecond- , getPastTimeMicrosecond- , getFutureTimeMicrosecond- , addMicroseconds- ) where+ Milliseconds (..),+ Microseconds (..),+ milliToMicro,+ microToMilli,+ TimeMicrosecond,+ timeMicrosecond0,+ getTimeMicrosecond,+ getElapsedTimeMicrosecond,+ elapsedTimeMicrosecond,+ getTimeoutInMicrosecond,+ getPastTimeMicrosecond,+ getFutureTimeMicrosecond,+ addMicroseconds,+) where import Data.UnixTime-import Foreign.C.Types (CTime(..))+import Foreign.C.Types (CTime (..)) import Network.QUIC.Imports @@ -27,10 +27,10 @@ newtype Microseconds = Microseconds Int deriving (Eq, Ord, Num, Bits) instance Show Milliseconds where- show (Milliseconds n) = show n+ show (Milliseconds n) = show n instance Show Microseconds where- show (Microseconds n) = show n+ show (Microseconds n) = show n {-# INLINE milliToMicro #-} milliToMicro :: Milliseconds -> Microseconds
Network/QUIC/Utils.hs view
@@ -6,7 +6,7 @@ import qualified Data.ByteString as BS import Data.ByteString.Base16 import qualified Data.ByteString.Char8 as C8-import Data.ByteString.Internal (ByteString(..))+import Data.ByteString.Internal (ByteString (..)) import Data.ByteString.Short (ShortByteString) import qualified Data.ByteString.Short as Short import Data.Char (chr)@@ -20,7 +20,7 @@ -- GHC 8.0 does not provide fromRight. fromRight :: b -> Either a b -> b fromRight _ (Right b) = b-fromRight b _ = b+fromRight b _ = b dec16 :: ByteString -> ByteString dec16 = fromRight "" . decode@@ -52,7 +52,7 @@ withByteString :: ByteString -> (Ptr Word8 -> IO a) -> IO a withByteString (PS fptr off _) f = withForeignPtr fptr $ \ptr ->- f (ptr `plusPtr` off)+ f (ptr `plusPtr` off) shortpack :: String -> ShortByteString shortpack = Short.toShort . C8.pack
Network/QUIC/Windows.hs view
@@ -1,6 +1,6 @@ module Network.QUIC.Windows (- windowsThreadBlockHack- ) where+ windowsThreadBlockHack,+) where import Control.Concurrent import qualified Control.Exception as CE@@ -13,5 +13,5 @@ void . forkIO $ CE.try act >>= putMVar var res <- takeMVar var case res of- Left e -> print e >> CE.throwIO e- Right r -> return r+ Left e -> print e >> CE.throwIO e+ Right r -> return r
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
quic.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name: quic-version: 0.1.8+version: 0.1.9 license: BSD3 license-file: LICENSE maintainer: kazu@iij.ad.jp@@ -138,9 +138,9 @@ iproute >=1.7.8, memory, network >=3.1.2,+ network-control, network-udp, network-byte-order >=0.1.5,- psqueues, random >=1.2, stm, tls >=1.7.0,
test/Config.hs view
@@ -1,17 +1,16 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Config (- makeTestServerConfig- , makeTestServerConfigR- , testClientConfig- , testClientConfigR- , setServerQlog- , setClientQlog- , withPipe- , Scenario(..)- , newSessionManager- ) where+ makeTestServerConfig,+ makeTestServerConfigR,+ testClientConfig,+ testClientConfigR,+ setServerQlog,+ setClientQlog,+ withPipe,+ Scenario (..),+ newSessionManager,+) where import Control.Monad import Data.ByteString (ByteString)@@ -19,7 +18,13 @@ import qualified Data.List as L import Network.Socket import Network.Socket.ByteString-import Network.TLS (Credentials(..), credentialLoadX509, SessionManager(..), SessionData, SessionID)+import Network.TLS (+ Credentials (..),+ SessionData,+ SessionID,+ SessionManager (..),+ credentialLoadX509,+ ) import UnliftIO.Concurrent import qualified UnliftIO.Exception as E @@ -28,49 +33,59 @@ makeTestServerConfig :: IO ServerConfig makeTestServerConfig = do- cred <- either error id <$> credentialLoadX509 "test/servercert.pem" "test/serverkey.pem"+ cred <-+ either error id+ <$> credentialLoadX509 "test/servercert.pem" "test/serverkey.pem" let credentials = Credentials [cred]- return testServerConfig {- scCredentials = credentials- , scALPN = Just chooseALPN- }+ return+ testServerConfig+ { scCredentials = credentials+ , scALPN = Just chooseALPN+ } testServerConfig :: ServerConfig-testServerConfig = defaultServerConfig {- -- Don't use "0.0.0.0" and "::" for Windows (UDP dispatching bug)- scAddresses = [("127.0.0.1",50003)]- }+testServerConfig =+ defaultServerConfig+ { -- Don't use "0.0.0.0" and "::" for Windows (UDP dispatching bug)+ scAddresses = [("127.0.0.1", 50003)]+ } makeTestServerConfigR :: IO ServerConfig makeTestServerConfigR = do- cred <- either error id <$> credentialLoadX509 "test/servercert.pem" "test/serverkey.pem"+ cred <-+ either error id+ <$> credentialLoadX509 "test/servercert.pem" "test/serverkey.pem" let credentials = Credentials [cred]- return testServerConfigR {- scCredentials = credentials- , scALPN = Just chooseALPN- }+ return+ testServerConfigR+ { scCredentials = credentials+ , scALPN = Just chooseALPN+ } testServerConfigR :: ServerConfig-testServerConfigR = defaultServerConfig {- -- Don't use "0.0.0.0" and "::" for Windows (UDP dispatching bug)- scAddresses = [("127.0.0.1",50003)]- }+testServerConfigR =+ defaultServerConfig+ { -- Don't use "0.0.0.0" and "::" for Windows (UDP dispatching bug)+ scAddresses = [("127.0.0.1", 50003)]+ } testClientConfig :: ClientConfig-testClientConfig = defaultClientConfig {- ccServerName = "127.0.0.1"- , ccPortName = "50003"- , ccValidate = False- , ccDebugLog = True- }+testClientConfig =+ defaultClientConfig+ { ccServerName = "127.0.0.1"+ , ccPortName = "50003"+ , ccValidate = False+ , ccDebugLog = True+ } testClientConfigR :: ClientConfig-testClientConfigR = defaultClientConfig {- ccServerName = "127.0.0.1"- , ccPortName = "50002"- , ccValidate = False- , ccDebugLog = True- }+testClientConfigR =+ defaultClientConfig+ { ccServerName = "127.0.0.1"+ , ccPortName = "50002"+ , ccValidate = False+ , ccDebugLog = True+ } setServerQlog :: ServerConfig -> ServerConfig setServerQlog sc = sc@@ -78,9 +93,10 @@ setClientQlog :: ClientConfig -> ClientConfig setClientQlog cc = cc -data Scenario = Randomly Int- | DropClientPacket [Int]- | DropServerPacket [Int]+data Scenario+ = Randomly Int+ | DropClientPacket [Int]+ | DropServerPacket [Int] withPipe :: Scenario -> IO () -> IO () withPipe scenario body = do@@ -91,65 +107,61 @@ irefC <- newIORef 0 irefS <- newIORef 0 E.bracket (openSocket addrC) close $ \sockC ->- E.bracket (openSocket addrS) close $ \sockS -> do- setSocketOption sockC ReuseAddr 1- setSocketOption sockS ReuseAddr 1- bind sockC saC- connect sockS saS- -- from client- tid0 <- forkIO $ do- (bs,saO) <- recvFrom sockC 2048- connect sockC saO- n0 <- atomicModifyIORef' irefC $ \x -> (x + 1, x)- dropPacket0 <- shouldDrop scenario True n0- unless dropPacket0 $ void $ send sockS bs- forever $ do- bs1 <--#if defined(mingw32_HOST_OS)- windowsThreadBlockHack $-#endif- recv sockC 2048- n <- atomicModifyIORef' irefC $ \x -> (x + 1, x)- dropPacket <- shouldDrop scenario True n- unless dropPacket $ void $ send sockS bs1- -- from server- tid1 <- forkIO $ forever $ do- bs <--#if defined(mingw32_HOST_OS)- windowsThreadBlockHack $-#endif+ E.bracket (openSocket addrS) close $ \sockS -> do+ setSocketOption sockC ReuseAddr 1+ setSocketOption sockS ReuseAddr 1+ bind sockC saC+ connect sockS saS+ -- from client+ tid0 <- forkIO $ do+ (bs, saO) <- recvFrom sockC 2048+ connect sockC saO+ n0 <- atomicModifyIORef' irefC $ \x -> (x + 1, x)+ dropPacket0 <- shouldDrop scenario True n0+ unless dropPacket0 $ void $ send sockS bs+ forever $ do+ bs1 <-+ recv sockC 2048+ n <- atomicModifyIORef' irefC $ \x -> (x + 1, x)+ dropPacket <- shouldDrop scenario True n+ unless dropPacket $ void $ send sockS bs1+ -- from server+ tid1 <- forkIO $ forever $ do+ bs <- recv sockS 2048- n <- atomicModifyIORef' irefS $ \x -> (x + 1, x)- dropPacket <- shouldDrop scenario False n- unless dropPacket $ void $ send sockC bs- body- killThread tid0- killThread tid1+ n <- atomicModifyIORef' irefS $ \x -> (x + 1, x)+ dropPacket <- shouldDrop scenario False n+ unless dropPacket $ void $ send sockC bs+ body+ killThread tid0+ killThread tid1 where- hints = defaultHints { addrSocketType = Datagram- , addrFlags = [AI_NUMERICHOST]- , addrFamily = AF_INET- }+ hints =+ defaultHints+ { addrSocketType = Datagram+ , addrFlags = [AI_NUMERICHOST]+ , addrFamily = AF_INET+ } resolve port = head <$> getAddrInfo (Just hints) (Just "127.0.0.1") (Just port) shouldDrop (Randomly n) _ _ = do w <- getRandomOneByte return ((w `mod` fromIntegral n) == 0) shouldDrop (DropClientPacket ns) fromC pn- | fromC = return (pn `elem` ns)- | otherwise = return False+ | fromC = return (pn `elem` ns)+ | otherwise = return False shouldDrop (DropServerPacket ns) fromC pn- | fromC = return False- | otherwise = return (pn `elem` ns)+ | fromC = return False+ | otherwise = return (pn `elem` ns) chooseALPN :: Version -> [ByteString] -> IO ByteString chooseALPN _ver protos = return $ case mh3idx of- Nothing -> case mhqidx of- Nothing -> ""- Just _ -> "hq"- Just h3idx -> case mhqidx of- Nothing -> "h3"- Just hqidx -> if h3idx < hqidx then "h3" else "hq"+ Nothing -> case mhqidx of+ Nothing -> ""+ Just _ -> "hq"+ Just h3idx -> case mhqidx of+ Nothing -> "h3"+ Just hqidx -> if h3idx < hqidx then "h3" else "hq" where mh3idx = "h3" `L.elemIndex` protos mhqidx = "hq" `L.elemIndex` protos@@ -158,18 +170,19 @@ newSessionManager = sessionManager <$> newIORef Nothing sessionManager :: IORef (Maybe (SessionID, SessionData)) -> SessionManager-sessionManager ref = SessionManager {- sessionEstablish = establish- , sessionResume = resume- , sessionResumeOnlyOnce = resume- , sessionInvalidate = \_ -> return ()- }+sessionManager ref =+ SessionManager+ { sessionEstablish = establish+ , sessionResume = resume+ , sessionResumeOnlyOnce = resume+ , sessionInvalidate = \_ -> return ()+ } where- establish sid sdata = writeIORef ref $ Just (sid,sdata)+ establish sid sdata = writeIORef ref $ Just (sid, sdata) resume sid = do mx <- readIORef ref case mx of- Nothing -> return Nothing- Just (s,d)- | s == sid -> return $ Just d- | otherwise -> return Nothing+ Nothing -> return Nothing+ Just (s, d)+ | s == sid -> return $ Just d+ | otherwise -> return Nothing
test/ErrorSpec.hs view
@@ -16,9 +16,11 @@ setup = do sc' <- makeTestServerConfig smgr <- newSessionManager- let sc = sc' { scSessionManager = smgr- , scUse0RTT = True- }+ let sc =+ sc'+ { scSessionManager = smgr+ , scUse0RTT = True+ } tid <- forkIO $ run sc loop threadDelay 500000 -- give enough time to the server return tid@@ -29,4 +31,5 @@ teardown tid = killThread tid spec :: Spec-spec = beforeAll setup $ afterAll teardown $ transportErrorSpec testClientConfig 2000 -- 2 seconds+spec =+ beforeAll setup $ afterAll teardown $ transportErrorSpec testClientConfig 2000 -- 2 seconds
test/FrameSpec.hs view
@@ -17,7 +17,7 @@ let siz = 512 E.bracket (mallocBytes siz) free $ \beg -> do let (+.) = plusPtr- end = beg +. siz+ end = beg +. siz fillBytes beg 0 $ fromIntegral siz countZero beg end `shouldReturn` siz countZero (beg +. 1) end `shouldReturn` (siz - 1)
test/HandshakeSpec.hs view
@@ -4,7 +4,7 @@ import Control.Monad import qualified Data.ByteString as BS-import Network.TLS (HandshakeMode13(..), Group(..))+import Network.TLS (Group (..), HandshakeMode13 (..)) import qualified Network.TLS as TLS import Test.Hspec import UnliftIO.Concurrent@@ -22,11 +22,14 @@ sc0' <- runIO makeTestServerConfig smgr <- runIO newSessionManager var <- runIO newEmptyMVar- let sc0 = sc0' { scSessionManager = smgr- , scHooks = (scHooks sc0') {- onServerReady = putMVar var ()- }- }+ let sc0 =+ sc0'+ { scSessionManager = smgr+ , scHooks =+ (scHooks sc0')+ { onServerReady = putMVar var ()+ }+ } let waitS = takeMVar var :: IO () describe "handshake" $ do it "can handshake in the normal case" $ do@@ -35,11 +38,11 @@ testHandshake cc sc waitS FullHandshake it "can handshake in the case of TLS hello retry" $ do let cc = testClientConfig- sc = sc0 { scGroups = [P256] }+ sc = sc0{scGroups = [P256]} testHandshake cc sc waitS HelloRetryRequest it "can handshake in the case of QUIC retry" $ do let cc = testClientConfig- sc = sc0 { scRequireRetry = True }+ sc = sc0{scRequireRetry = True} testHandshake cc sc waitS FullHandshake it "can handshake in the case of resumption" $ do let cc = testClientConfig@@ -47,46 +50,52 @@ testHandshake2 cc sc waitS (FullHandshake, PreSharedKey) False it "can handshake in the case of 0-RTT" $ do let cc = testClientConfig- sc = sc0 { scUse0RTT = True }+ sc = sc0{scUse0RTT = True} testHandshake2 cc sc waitS (FullHandshake, RTT0) True it "fails with unknown server certificate" $ do- let cc1 = testClientConfig {- ccValidate = True -- ouch, default should be reversed- }+ let cc1 =+ testClientConfig+ { ccValidate = True -- ouch, default should be reversed+ } cc2 = testClientConfig- sc = sc0+ sc = sc0 certificateRejected e- | TransportErrorIsSent te@(TransportError _) _ <- e = te == cryptoError TLS.CertificateUnknown+ | TransportErrorIsSent te@(TransportError _) _ <- e =+ te == cryptoError TLS.CertificateUnknown | otherwise = False testHandshake3 cc1 cc2 sc waitS certificateRejected it "fails with no group in common" $ do- let cc1 = testClientConfig { ccGroups = [X25519] }- cc2 = testClientConfig { ccGroups = [P256] }- sc = sc0 { scGroups = [P256] }+ let cc1 = testClientConfig{ccGroups = [X25519]}+ cc2 = testClientConfig{ccGroups = [P256]}+ sc = sc0{scGroups = [P256]} handshakeFailure e- | TransportErrorIsReceived te@(TransportError _) _ <- e = te == cryptoError TLS.HandshakeFailure+ | TransportErrorIsReceived te@(TransportError _) _ <- e =+ te == cryptoError TLS.HandshakeFailure | otherwise = False testHandshake3 cc1 cc2 sc waitS handshakeFailure it "can handshake with large HE from a client" $ do let cc0 = testClientConfig- params = (ccParameters cc0) {- grease = Just (BS.pack (replicate 2400 0))- }- cc = cc0 { ccParameters = params }+ params =+ (ccParameters cc0)+ { grease = Just (BS.pack (replicate 2400 0))+ }+ cc = cc0{ccParameters = params} sc = sc0 testHandshake cc sc waitS FullHandshake it "can handshake with large EE from a server (3-times rule)" $ do let cc = testClientConfig- params = (scParameters sc0) {- grease = Just (BS.pack (replicate 3800 0))- }- sc = sc0 { scParameters = params }+ params =+ (scParameters sc0)+ { grease = Just (BS.pack (replicate 3800 0))+ }+ sc = sc0{scParameters = params} testHandshake cc sc waitS FullHandshake onE :: IO b -> IO a -> IO a onE h b = E.onException b h -testHandshake :: ClientConfig -> ServerConfig -> IO () -> HandshakeMode13 -> IO ()+testHandshake+ :: ClientConfig -> ServerConfig -> IO () -> HandshakeMode13 -> IO () testHandshake cc sc waitS mode = do mvar <- newEmptyMVar E.bracket (forkIO $ server mvar) killThread $ \_ -> client mvar@@ -110,7 +119,13 @@ shutdownStream s void $ recvStream s 1024 -testHandshake2 :: ClientConfig -> ServerConfig -> IO () -> (HandshakeMode13, HandshakeMode13) -> Bool -> IO ()+testHandshake2+ :: ClientConfig+ -> ServerConfig+ -> IO ()+ -> (HandshakeMode13, HandshakeMode13)+ -> Bool+ -> IO () testHandshake2 cc1 sc waitS (mode1, mode2) use0RTT = do mvar <- newEmptyMVar E.bracket (forkIO $ server mvar) killThread $ \_ -> client mvar@@ -124,9 +139,11 @@ waitS res <- runClient cc1 mode1 $ query "first" threadDelay 50000- let cc2 = cc1 { ccResumption = res- , ccUse0RTT = use0RTT- }+ let cc2 =+ cc1+ { ccResumption = res+ , ccUse0RTT = use0RTT+ } void $ runClient cc2 mode2 $ query "second" takeMVar mvar server mvar = S.run sc serv@@ -136,16 +153,22 @@ bs <- recvStream s 1024 sendStream s "bye" closeStream s- when (bs == "second") $ putMVar mvar ()+ when (bs == "second") $ putMVar mvar () -testHandshake3 :: ClientConfig -> ClientConfig -> ServerConfig -> IO () -> (QUICException -> Bool) -> IO ()+testHandshake3+ :: ClientConfig+ -> ClientConfig+ -> ServerConfig+ -> IO ()+ -> (QUICException -> Bool)+ -> IO () testHandshake3 cc1 cc2 sc waitS selector = do mvar <- newEmptyMVar E.bracket (forkIO $ server mvar) killThread $ \_ -> client mvar where client mvar = do waitS- C.run cc1 (query "first") `shouldThrow` selector+ C.run cc1 (query "first") `shouldThrow` selector C.run cc2 (query "second") `shouldReturn` () takeMVar mvar server mvar = S.run sc $ \conn -> do
test/IOSpec.hs view
@@ -19,10 +19,13 @@ spec = do sc0 <- runIO makeTestServerConfigR var <- runIO newEmptyMVar- let sc = sc0 { scHooks = (scHooks sc0) {- onServerReady = putMVar var ()- }- }+ let sc =+ sc0+ { scHooks =+ (scHooks sc0)+ { onServerReady = putMVar var ()+ }+ } let cc = testClientConfigR let waitS = takeMVar var :: IO () describe "send & recv" $ do@@ -95,7 +98,8 @@ assertEndOfStream :: Stream -> IO () assertEndOfStream strm = recvStream strm 1024 `shouldReturn` "" -testRecvStreamClientStopFirst :: C.ClientConfig -> ServerConfig -> IO () -> IO ()+testRecvStreamClientStopFirst+ :: C.ClientConfig -> ServerConfig -> IO () -> IO () testRecvStreamClientStopFirst cc sc waitS = do mvar <- newEmptyMVar E.bracket (forkIO $ server mvar) killThread $ \_ -> client mvar@@ -120,7 +124,8 @@ assertEndOfStream strm putMVar mvar () -testRecvStreamServerStopFirst :: C.ClientConfig -> ServerConfig -> IO () -> IO ()+testRecvStreamServerStopFirst+ :: C.ClientConfig -> ServerConfig -> IO () -> IO () testRecvStreamServerStopFirst cc sc waitS = do mvar <- newEmptyMVar E.bracket (forkIO $ server mvar) killThread $ \_ -> client mvar
test/PacketSpec.hs view
@@ -39,11 +39,13 @@ let noLog _ = return () let serverCID = clientChosenCID clientCID = toCID ""- serverAuthCIDs = defaultAuthCIDs { initSrcCID = Just serverCID- , origDstCID = Just serverCID- }- clientAuthCIDs = defaultAuthCIDs { initSrcCID = Just clientCID }- -- dummy+ serverAuthCIDs =+ defaultAuthCIDs+ { initSrcCID = Just serverCID+ , origDstCID = Just serverCID+ }+ clientAuthCIDs = defaultAuthCIDs{initSrcCID = Just clientCID}+ -- dummy let clientConf = testClientConfig us <- clientSocket "127.0.0.1" "2000" False q <- newRecvQ@@ -51,15 +53,39 @@ let ver = v verInfo = VersionInfo ver [ver] ----- clientConn <- clientConnection clientConf verInfo clientAuthCIDs serverAuthCIDs noLog noLog defaultHooks sref q undefined undefined+ clientConn <-+ clientConnection+ clientConf+ verInfo+ clientAuthCIDs+ serverAuthCIDs+ noLog+ noLog+ defaultHooks+ sref+ q+ undefined+ undefined initializeCoder clientConn InitialLevel $ initialSecrets ver serverCID- serverConn <- serverConnection conf verInfo serverAuthCIDs clientAuthCIDs noLog noLog defaultHooks sref q undefined undefined+ serverConn <-+ serverConnection+ conf+ verInfo+ serverAuthCIDs+ clientAuthCIDs+ noLog+ noLog+ defaultHooks+ sref+ q+ undefined+ undefined initializeCoder serverConn InitialLevel $ initialSecrets ver serverCID ---- return (clientConn, serverConn) -checkBinary :: (Connection,Connection)-> PacketNumber -> ByteString -> IO ()-checkBinary (senderConn,recverConn) pn bin = do+checkBinary :: (Connection, Connection) -> PacketNumber -> ByteString -> IO ()+checkBinary (senderConn, recverConn) pn bin = do ---- Decoding by the receiver (PacketIC (CryptPacket header crypt) lvl _, _) <- decodePacket bin ---- Cecrypting by the receiver@@ -79,103 +105,111 @@ plainFrames plain' `shouldBe` plainFrames plain clientInitialPacketBinaryV1 :: ByteString-clientInitialPacketBinaryV1 = dec16 $ BS.concat [- "c000000001088394c8f03e5157080000449e7b9aec34d1b1c98dd7689fb8ec11"- , "d242b123dc9bd8bab936b47d92ec356c0bab7df5976d27cd449f63300099f399"- , "1c260ec4c60d17b31f8429157bb35a1282a643a8d2262cad67500cadb8e7378c"- , "8eb7539ec4d4905fed1bee1fc8aafba17c750e2c7ace01e6005f80fcb7df6212"- , "30c83711b39343fa028cea7f7fb5ff89eac2308249a02252155e2347b63d58c5"- , "457afd84d05dfffdb20392844ae812154682e9cf012f9021a6f0be17ddd0c208"- , "4dce25ff9b06cde535d0f920a2db1bf362c23e596d11a4f5a6cf3948838a3aec"- , "4e15daf8500a6ef69ec4e3feb6b1d98e610ac8b7ec3faf6ad760b7bad1db4ba3"- , "485e8a94dc250ae3fdb41ed15fb6a8e5eba0fc3dd60bc8e30c5c4287e53805db"- , "059ae0648db2f64264ed5e39be2e20d82df566da8dd5998ccabdae053060ae6c"- , "7b4378e846d29f37ed7b4ea9ec5d82e7961b7f25a9323851f681d582363aa5f8"- , "9937f5a67258bf63ad6f1a0b1d96dbd4faddfcefc5266ba6611722395c906556"- , "be52afe3f565636ad1b17d508b73d8743eeb524be22b3dcbc2c7468d54119c74"- , "68449a13d8e3b95811a198f3491de3e7fe942b330407abf82a4ed7c1b311663a"- , "c69890f4157015853d91e923037c227a33cdd5ec281ca3f79c44546b9d90ca00"- , "f064c99e3dd97911d39fe9c5d0b23a229a234cb36186c4819e8b9c5927726632"- , "291d6a418211cc2962e20fe47feb3edf330f2c603a9d48c0fcb5699dbfe58964"- , "25c5bac4aee82e57a85aaf4e2513e4f05796b07ba2ee47d80506f8d2c25e50fd"- , "14de71e6c418559302f939b0e1abd576f279c4b2e0feb85c1f28ff18f58891ff"- , "ef132eef2fa09346aee33c28eb130ff28f5b766953334113211996d20011a198"- , "e3fc433f9f2541010ae17c1bf202580f6047472fb36857fe843b19f5984009dd"- , "c324044e847a4f4a0ab34f719595de37252d6235365e9b84392b061085349d73"- , "203a4a13e96f5432ec0fd4a1ee65accdd5e3904df54c1da510b0ff20dcc0c77f"- , "cb2c0e0eb605cb0504db87632cf3d8b4dae6e705769d1de354270123cb11450e"- , "fc60ac47683d7b8d0f811365565fd98c4c8eb936bcab8d069fc33bd801b03ade"- , "a2e1fbc5aa463d08ca19896d2bf59a071b851e6c239052172f296bfb5e724047"- , "90a2181014f3b94a4e97d117b438130368cc39dbb2d198065ae3986547926cd2"- , "162f40a29f0c3c8745c0f50fba3852e566d44575c29d39a03f0cda721984b6f4"- , "40591f355e12d439ff150aab7613499dbd49adabc8676eef023b15b65bfc5ca0"- , "6948109f23f350db82123535eb8a7433bdabcb909271a6ecbcb58b936a88cd4e"- , "8f2e6ff5800175f113253d8fa9ca8885c2f552e657dc603f252e1a8e308f76f0"- , "be79e2fb8f5d5fbbe2e30ecadd220723c8c0aea8078cdfcb3868263ff8f09400"- , "54da48781893a7e49ad5aff4af300cd804a6b6279ab3ff3afb64491c85194aab"- , "760d58a606654f9f4400e8b38591356fbf6425aca26dc85244259ff2b19c41b9"- , "f96f3ca9ec1dde434da7d2d392b905ddf3d1f9af93d1af5950bd493f5aa731b4"- , "056df31bd267b6b90a079831aaf579be0a39013137aac6d404f518cfd4684064"- , "7e78bfe706ca4cf5e9c5453e9f7cfd2b8b4c8d169a44e55c88d4a9a7f9474241"- , "e221af44860018ab0856972e194cd934"- ]+clientInitialPacketBinaryV1 =+ dec16 $+ BS.concat+ [ "c000000001088394c8f03e5157080000449e7b9aec34d1b1c98dd7689fb8ec11"+ , "d242b123dc9bd8bab936b47d92ec356c0bab7df5976d27cd449f63300099f399"+ , "1c260ec4c60d17b31f8429157bb35a1282a643a8d2262cad67500cadb8e7378c"+ , "8eb7539ec4d4905fed1bee1fc8aafba17c750e2c7ace01e6005f80fcb7df6212"+ , "30c83711b39343fa028cea7f7fb5ff89eac2308249a02252155e2347b63d58c5"+ , "457afd84d05dfffdb20392844ae812154682e9cf012f9021a6f0be17ddd0c208"+ , "4dce25ff9b06cde535d0f920a2db1bf362c23e596d11a4f5a6cf3948838a3aec"+ , "4e15daf8500a6ef69ec4e3feb6b1d98e610ac8b7ec3faf6ad760b7bad1db4ba3"+ , "485e8a94dc250ae3fdb41ed15fb6a8e5eba0fc3dd60bc8e30c5c4287e53805db"+ , "059ae0648db2f64264ed5e39be2e20d82df566da8dd5998ccabdae053060ae6c"+ , "7b4378e846d29f37ed7b4ea9ec5d82e7961b7f25a9323851f681d582363aa5f8"+ , "9937f5a67258bf63ad6f1a0b1d96dbd4faddfcefc5266ba6611722395c906556"+ , "be52afe3f565636ad1b17d508b73d8743eeb524be22b3dcbc2c7468d54119c74"+ , "68449a13d8e3b95811a198f3491de3e7fe942b330407abf82a4ed7c1b311663a"+ , "c69890f4157015853d91e923037c227a33cdd5ec281ca3f79c44546b9d90ca00"+ , "f064c99e3dd97911d39fe9c5d0b23a229a234cb36186c4819e8b9c5927726632"+ , "291d6a418211cc2962e20fe47feb3edf330f2c603a9d48c0fcb5699dbfe58964"+ , "25c5bac4aee82e57a85aaf4e2513e4f05796b07ba2ee47d80506f8d2c25e50fd"+ , "14de71e6c418559302f939b0e1abd576f279c4b2e0feb85c1f28ff18f58891ff"+ , "ef132eef2fa09346aee33c28eb130ff28f5b766953334113211996d20011a198"+ , "e3fc433f9f2541010ae17c1bf202580f6047472fb36857fe843b19f5984009dd"+ , "c324044e847a4f4a0ab34f719595de37252d6235365e9b84392b061085349d73"+ , "203a4a13e96f5432ec0fd4a1ee65accdd5e3904df54c1da510b0ff20dcc0c77f"+ , "cb2c0e0eb605cb0504db87632cf3d8b4dae6e705769d1de354270123cb11450e"+ , "fc60ac47683d7b8d0f811365565fd98c4c8eb936bcab8d069fc33bd801b03ade"+ , "a2e1fbc5aa463d08ca19896d2bf59a071b851e6c239052172f296bfb5e724047"+ , "90a2181014f3b94a4e97d117b438130368cc39dbb2d198065ae3986547926cd2"+ , "162f40a29f0c3c8745c0f50fba3852e566d44575c29d39a03f0cda721984b6f4"+ , "40591f355e12d439ff150aab7613499dbd49adabc8676eef023b15b65bfc5ca0"+ , "6948109f23f350db82123535eb8a7433bdabcb909271a6ecbcb58b936a88cd4e"+ , "8f2e6ff5800175f113253d8fa9ca8885c2f552e657dc603f252e1a8e308f76f0"+ , "be79e2fb8f5d5fbbe2e30ecadd220723c8c0aea8078cdfcb3868263ff8f09400"+ , "54da48781893a7e49ad5aff4af300cd804a6b6279ab3ff3afb64491c85194aab"+ , "760d58a606654f9f4400e8b38591356fbf6425aca26dc85244259ff2b19c41b9"+ , "f96f3ca9ec1dde434da7d2d392b905ddf3d1f9af93d1af5950bd493f5aa731b4"+ , "056df31bd267b6b90a079831aaf579be0a39013137aac6d404f518cfd4684064"+ , "7e78bfe706ca4cf5e9c5453e9f7cfd2b8b4c8d169a44e55c88d4a9a7f9474241"+ , "e221af44860018ab0856972e194cd934"+ ] clientInitialPacketBinaryV2 :: ByteString-clientInitialPacketBinaryV2 = dec16 $ BS.concat [- "d76b3343cf088394c8f03e5157080000449ea0c95e82ffe67b6abcdb4298b485"- , "dd04de806071bf03dceebfa162e75d6c96058bdbfb127cdfcbf903388e99ad04"- , "9f9a3dd4425ae4d0992cfff18ecf0fdb5a842d09747052f17ac2053d21f57c5d"- , "250f2c4f0e0202b70785b7946e992e58a59ac52dea6774d4f03b55545243cf1a"- , "12834e3f249a78d395e0d18f4d766004f1a2674802a747eaa901c3f10cda5500"- , "cb9122faa9f1df66c392079a1b40f0de1c6054196a11cbea40afb6ef5253cd68"- , "18f6625efce3b6def6ba7e4b37a40f7732e093daa7d52190935b8da58976ff33"- , "12ae50b187c1433c0f028edcc4c2838b6a9bfc226ca4b4530e7a4ccee1bfa2a3"- , "d396ae5a3fb512384b2fdd851f784a65e03f2c4fbe11a53c7777c023462239dd"- , "6f7521a3f6c7d5dd3ec9b3f233773d4b46d23cc375eb198c63301c21801f6520"- , "bcfb7966fc49b393f0061d974a2706df8c4a9449f11d7f3d2dcbb90c6b877045"- , "636e7c0c0fe4eb0f697545460c806910d2c355f1d253bc9d2452aaa549e27a1f"- , "ac7cf4ed77f322e8fa894b6a83810a34b361901751a6f5eb65a0326e07de7c12"- , "16ccce2d0193f958bb3850a833f7ae432b65bc5a53975c155aa4bcb4f7b2c4e5"- , "4df16efaf6ddea94e2c50b4cd1dfe06017e0e9d02900cffe1935e0491d77ffb4"- , "fdf85290fdd893d577b1131a610ef6a5c32b2ee0293617a37cbb08b847741c3b"- , "8017c25ca9052ca1079d8b78aebd47876d330a30f6a8c6d61dd1ab5589329de7"- , "14d19d61370f8149748c72f132f0fc99f34d766c6938597040d8f9e2bb522ff9"- , "9c63a344d6a2ae8aa8e51b7b90a4a806105fcbca31506c446151adfeceb51b91"- , "abfe43960977c87471cf9ad4074d30e10d6a7f03c63bd5d4317f68ff325ba3bd"- , "80bf4dc8b52a0ba031758022eb025cdd770b44d6d6cf0670f4e990b22347a7db"- , "848265e3e5eb72dfe8299ad7481a408322cac55786e52f633b2fb6b614eaed18"- , "d703dd84045a274ae8bfa73379661388d6991fe39b0d93debb41700b41f90a15"- , "c4d526250235ddcd6776fc77bc97e7a417ebcb31600d01e57f32162a8560cacc"- , "7e27a096d37a1a86952ec71bd89a3e9a30a2a26162984d7740f81193e8238e61"- , "f6b5b984d4d3dfa033c1bb7e4f0037febf406d91c0dccf32acf423cfa1e70710"- , "10d3f270121b493ce85054ef58bada42310138fe081adb04e2bd901f2f13458b"- , "3d6758158197107c14ebb193230cd1157380aa79cae1374a7c1e5bbcb80ee23e"- , "06ebfde206bfb0fcbc0edc4ebec309661bdd908d532eb0c6adc38b7ca7331dce"- , "8dfce39ab71e7c32d318d136b6100671a1ae6a6600e3899f31f0eed19e3417d1"- , "34b90c9058f8632c798d4490da4987307cba922d61c39805d072b589bd52fdf1"- , "e86215c2d54e6670e07383a27bbffb5addf47d66aa85a0c6f9f32e59d85a44dd"- , "5d3b22dc2be80919b490437ae4f36a0ae55edf1d0b5cb4e9a3ecabee93dfc6e3"- , "8d209d0fa6536d27a5d6fbb17641cde27525d61093f1b28072d111b2b4ae5f89"- , "d5974ee12e5cf7d5da4d6a31123041f33e61407e76cffcdcfd7e19ba58cf4b53"- , "6f4c4938ae79324dc402894b44faf8afbab35282ab659d13c93f70412e85cb19"- , "9a37ddec600545473cfb5a05e08d0b209973b2172b4d21fb69745a262ccde96b"- , "a18b2faa745b6fe189cf772a9f84cbfc"- ]+clientInitialPacketBinaryV2 =+ dec16 $+ BS.concat+ [ "d76b3343cf088394c8f03e5157080000449ea0c95e82ffe67b6abcdb4298b485"+ , "dd04de806071bf03dceebfa162e75d6c96058bdbfb127cdfcbf903388e99ad04"+ , "9f9a3dd4425ae4d0992cfff18ecf0fdb5a842d09747052f17ac2053d21f57c5d"+ , "250f2c4f0e0202b70785b7946e992e58a59ac52dea6774d4f03b55545243cf1a"+ , "12834e3f249a78d395e0d18f4d766004f1a2674802a747eaa901c3f10cda5500"+ , "cb9122faa9f1df66c392079a1b40f0de1c6054196a11cbea40afb6ef5253cd68"+ , "18f6625efce3b6def6ba7e4b37a40f7732e093daa7d52190935b8da58976ff33"+ , "12ae50b187c1433c0f028edcc4c2838b6a9bfc226ca4b4530e7a4ccee1bfa2a3"+ , "d396ae5a3fb512384b2fdd851f784a65e03f2c4fbe11a53c7777c023462239dd"+ , "6f7521a3f6c7d5dd3ec9b3f233773d4b46d23cc375eb198c63301c21801f6520"+ , "bcfb7966fc49b393f0061d974a2706df8c4a9449f11d7f3d2dcbb90c6b877045"+ , "636e7c0c0fe4eb0f697545460c806910d2c355f1d253bc9d2452aaa549e27a1f"+ , "ac7cf4ed77f322e8fa894b6a83810a34b361901751a6f5eb65a0326e07de7c12"+ , "16ccce2d0193f958bb3850a833f7ae432b65bc5a53975c155aa4bcb4f7b2c4e5"+ , "4df16efaf6ddea94e2c50b4cd1dfe06017e0e9d02900cffe1935e0491d77ffb4"+ , "fdf85290fdd893d577b1131a610ef6a5c32b2ee0293617a37cbb08b847741c3b"+ , "8017c25ca9052ca1079d8b78aebd47876d330a30f6a8c6d61dd1ab5589329de7"+ , "14d19d61370f8149748c72f132f0fc99f34d766c6938597040d8f9e2bb522ff9"+ , "9c63a344d6a2ae8aa8e51b7b90a4a806105fcbca31506c446151adfeceb51b91"+ , "abfe43960977c87471cf9ad4074d30e10d6a7f03c63bd5d4317f68ff325ba3bd"+ , "80bf4dc8b52a0ba031758022eb025cdd770b44d6d6cf0670f4e990b22347a7db"+ , "848265e3e5eb72dfe8299ad7481a408322cac55786e52f633b2fb6b614eaed18"+ , "d703dd84045a274ae8bfa73379661388d6991fe39b0d93debb41700b41f90a15"+ , "c4d526250235ddcd6776fc77bc97e7a417ebcb31600d01e57f32162a8560cacc"+ , "7e27a096d37a1a86952ec71bd89a3e9a30a2a26162984d7740f81193e8238e61"+ , "f6b5b984d4d3dfa033c1bb7e4f0037febf406d91c0dccf32acf423cfa1e70710"+ , "10d3f270121b493ce85054ef58bada42310138fe081adb04e2bd901f2f13458b"+ , "3d6758158197107c14ebb193230cd1157380aa79cae1374a7c1e5bbcb80ee23e"+ , "06ebfde206bfb0fcbc0edc4ebec309661bdd908d532eb0c6adc38b7ca7331dce"+ , "8dfce39ab71e7c32d318d136b6100671a1ae6a6600e3899f31f0eed19e3417d1"+ , "34b90c9058f8632c798d4490da4987307cba922d61c39805d072b589bd52fdf1"+ , "e86215c2d54e6670e07383a27bbffb5addf47d66aa85a0c6f9f32e59d85a44dd"+ , "5d3b22dc2be80919b490437ae4f36a0ae55edf1d0b5cb4e9a3ecabee93dfc6e3"+ , "8d209d0fa6536d27a5d6fbb17641cde27525d61093f1b28072d111b2b4ae5f89"+ , "d5974ee12e5cf7d5da4d6a31123041f33e61407e76cffcdcfd7e19ba58cf4b53"+ , "6f4c4938ae79324dc402894b44faf8afbab35282ab659d13c93f70412e85cb19"+ , "9a37ddec600545473cfb5a05e08d0b209973b2172b4d21fb69745a262ccde96b"+ , "a18b2faa745b6fe189cf772a9f84cbfc"+ ] serverInitialPacketBinaryV1 :: ByteString-serverInitialPacketBinaryV1 = dec16 $ BS.concat [- "cf000000010008f067a5502a4262b5004075c0d95a482cd0991cd25b0aac406a"- , "5816b6394100f37a1c69797554780bb38cc5a99f5ede4cf73c3ec2493a1839b3"- , "dbcba3f6ea46c5b7684df3548e7ddeb9c3bf9c73cc3f3bded74b562bfb19fb84"- , "022f8ef4cdd93795d77d06edbb7aaf2f58891850abbdca3d20398c276456cbc4"- , "2158407dd074ee"- ]+serverInitialPacketBinaryV1 =+ dec16 $+ BS.concat+ [ "cf000000010008f067a5502a4262b5004075c0d95a482cd0991cd25b0aac406a"+ , "5816b6394100f37a1c69797554780bb38cc5a99f5ede4cf73c3ec2493a1839b3"+ , "dbcba3f6ea46c5b7684df3548e7ddeb9c3bf9c73cc3f3bded74b562bfb19fb84"+ , "022f8ef4cdd93795d77d06edbb7aaf2f58891850abbdca3d20398c276456cbc4"+ , "2158407dd074ee"+ ] serverInitialPacketBinaryV2 :: ByteString-serverInitialPacketBinaryV2 = dec16 $ BS.concat [- "dc6b3343cf0008f067a5502a4262b5004075d92faaf16f05d8a4398c47089698"- , "baeea26b91eb761d9b89237bbf87263017915358230035f7fd3945d88965cf17"- , "f9af6e16886c61bfc703106fbaf3cb4cfa52382dd16a393e42757507698075b2"- , "c984c707f0a0812d8cd5a6881eaf21ceda98f4bd23f6fe1a3e2c43edd9ce7ca8"- , "4bed8521e2e140"- ]+serverInitialPacketBinaryV2 =+ dec16 $+ BS.concat+ [ "dc6b3343cf0008f067a5502a4262b5004075d92faaf16f05d8a4398c47089698"+ , "baeea26b91eb761d9b89237bbf87263017915358230035f7fd3945d88965cf17"+ , "f9af6e16886c61bfc703106fbaf3cb4cfa52382dd16a393e42757507698075b2"+ , "c984c707f0a0812d8cd5a6881eaf21ceda98f4bd23f6fe1a3e2c43edd9ce7ca8"+ , "4bed8521e2e140"+ ]
test/RecoverySpec.hs view
@@ -14,44 +14,99 @@ describe "persistent congestion" $ do it "does not find a pair" $ do findDuration (Seq.fromList [sp0]) 0 `shouldBe` Nothing- findDuration (Seq.fromList [sp0,sp1]) 0 `shouldBe` Nothing- findDuration (Seq.fromList [sp0,sp1,sp2]) 0 `shouldBe` Nothing- findDuration (Seq.fromList [sp0,sp2,sp2,sp3]) 0 `shouldBe` Nothing- findDuration (Seq.fromList [sp0,sp2,sp2,sp3,sp4]) 0 `shouldBe` Nothing- findDuration (Seq.fromList [sp0,sp2,sp2,sp3,sp5]) 0 `shouldBe` Nothing- findDuration (Seq.fromList [sp0,sp2,sp2,sp3,sp4,sp5]) 2 `shouldBe` Nothing+ findDuration (Seq.fromList [sp0, sp1]) 0 `shouldBe` Nothing+ findDuration (Seq.fromList [sp0, sp1, sp2]) 0 `shouldBe` Nothing+ findDuration (Seq.fromList [sp0, sp2, sp2, sp3]) 0 `shouldBe` Nothing+ findDuration (Seq.fromList [sp0, sp2, sp2, sp3, sp4]) 0 `shouldBe` Nothing+ findDuration (Seq.fromList [sp0, sp2, sp2, sp3, sp5]) 0 `shouldBe` Nothing+ findDuration (Seq.fromList [sp0, sp2, sp2, sp3, sp4, sp5]) 2 `shouldBe` Nothing it "finds a pair" $ do- findDuration (Seq.fromList [sp0,sp1,sp2,sp3,sp4,sp5]) 0 `shouldBe` Just (UnixDiffTime 4 0)- findDuration (Seq.fromList [sp0,sp1,sp2,sp3,sp4,sp5,sp6]) 0 `shouldBe` Just (UnixDiffTime 4 0)- findDuration (Seq.fromList [sp0,sp1,sp2,sp3,sp4,sp5,sp6,sp7]) 0 `shouldBe` Just (UnixDiffTime 6 0)- findDuration (Seq.fromList [sp0,sp1,sp2,sp3,sp4,sp5,sp6,sp7]) 3 `shouldBe` Just (UnixDiffTime 2 0)- findDuration (Seq.fromList [sp0,sp1,sp2,sp3,sp4,sp5,sp6,sp7,sp8,sp9,sp10]) 2 `shouldBe` Just (UnixDiffTime 5 0)- findDuration (Seq.fromList [sp0,sp1,sp2,sp3,sp4,sp5,sp6,sp7,sp8,sp9]) 0 `shouldBe` Just (UnixDiffTime 6 0)- findDuration (Seq.fromList [sp0,sp1,sp2,sp3,sp4,sp5,sp6,sp7,sp8,sp9,sp10]) 0 `shouldBe` Just (UnixDiffTime 9 0)- findDuration (Seq.fromList [sp0,sp1,sp2,sp3,sp4,sp5,sp6,sp7,sp8,sp9,sp10,sp20,sp21]) 0 `shouldBe` Just (UnixDiffTime 22 0)- findDuration (Seq.fromList [sp0,sp1,sp2,sp3,sp4,sp5,sp6,sp7,sp8,sp9,sp10,sp20,sp21,sp30,sp31,sp32,sp33]) 0 `shouldBe` Just (UnixDiffTime 29 0)+ findDuration (Seq.fromList [sp0, sp1, sp2, sp3, sp4, sp5]) 0+ `shouldBe` Just (UnixDiffTime 4 0)+ findDuration (Seq.fromList [sp0, sp1, sp2, sp3, sp4, sp5, sp6]) 0+ `shouldBe` Just (UnixDiffTime 4 0)+ findDuration (Seq.fromList [sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7]) 0+ `shouldBe` Just (UnixDiffTime 6 0)+ findDuration (Seq.fromList [sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7]) 3+ `shouldBe` Just (UnixDiffTime 2 0)+ findDuration+ (Seq.fromList [sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7, sp8, sp9, sp10])+ 2+ `shouldBe` Just (UnixDiffTime 5 0)+ findDuration (Seq.fromList [sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7, sp8, sp9]) 0+ `shouldBe` Just (UnixDiffTime 6 0)+ findDuration+ (Seq.fromList [sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7, sp8, sp9, sp10])+ 0+ `shouldBe` Just (UnixDiffTime 9 0)+ findDuration+ ( Seq.fromList+ [sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7, sp8, sp9, sp10, sp20, sp21]+ )+ 0+ `shouldBe` Just (UnixDiffTime 22 0)+ findDuration+ ( Seq.fromList+ [ sp0+ , sp1+ , sp2+ , sp3+ , sp4+ , sp5+ , sp6+ , sp7+ , sp8+ , sp9+ , sp10+ , sp20+ , sp21+ , sp30+ , sp31+ , sp32+ , sp33+ ]+ )+ 0+ `shouldBe` Just (UnixDiffTime 29 0) -sp0,sp1,sp2,sp3,sp4,sp5,sp6,sp7,sp8,sp9,sp10,sp20,sp21,sp30,sp31,sp32,sp33 :: SentPacket-sp0 = sp 0 False $ UnixTime 0 0-sp1 = sp 1 True $ UnixTime 1 0-sp2 = sp 2 False $ UnixTime 2 0-sp3 = sp 3 False $ UnixTime 3 0-sp4 = sp 4 False $ UnixTime 4 0-sp5 = sp 5 True $ UnixTime 5 0-sp6 = sp 6 False $ UnixTime 6 0-sp7 = sp 7 True $ UnixTime 7 0-sp8 = sp 8 False $ UnixTime 8 0-sp9 = sp 9 False $ UnixTime 9 0-sp10 = sp 10 True $ UnixTime 10 0-sp20 = sp 20 True $ UnixTime 22 0-sp21 = sp 21 True $ UnixTime 44 0+sp0+ , sp1+ , sp2+ , sp3+ , sp4+ , sp5+ , sp6+ , sp7+ , sp8+ , sp9+ , sp10+ , sp20+ , sp21+ , sp30+ , sp31+ , sp32+ , sp33+ :: SentPacket+sp0 = sp 0 False $ UnixTime 0 0+sp1 = sp 1 True $ UnixTime 1 0+sp2 = sp 2 False $ UnixTime 2 0+sp3 = sp 3 False $ UnixTime 3 0+sp4 = sp 4 False $ UnixTime 4 0+sp5 = sp 5 True $ UnixTime 5 0+sp6 = sp 6 False $ UnixTime 6 0+sp7 = sp 7 True $ UnixTime 7 0+sp8 = sp 8 False $ UnixTime 8 0+sp9 = sp 9 False $ UnixTime 9 0+sp10 = sp 10 True $ UnixTime 10 0+sp20 = sp 20 True $ UnixTime 22 0+sp21 = sp 21 True $ UnixTime 44 0 sp30 = sp 30 False $ UnixTime 50 0-sp31 = sp 31 True $ UnixTime 51 0+sp31 = sp 31 True $ UnixTime 51 0 sp32 = sp 32 False $ UnixTime 55 0-sp33 = sp 33 True $ UnixTime 80 0+sp33 = sp 33 True $ UnixTime 80 0 sp :: PacketNumber -> Bool -> TimeMicrosecond -> SentPacket-sp mypn ackeli tm = spkt { spTimeSent = tm }+sp mypn ackeli tm = spkt{spTimeSent = tm} where ppkt = PlainPacket (Short $ toCID "") (Plain (Flags 0) 0 [] 0) spkt = mkSentPacket mypn InitialLevel ppkt emptyPeerPacketNumbers ackeli
test/TLSSpec.hs view
@@ -29,7 +29,9 @@ -- shared keys let dcID = makeCID (dec16s "8394c8f03e515708") let client_initial_secret@(ClientTrafficSecret cis) = clientInitialSecret ver dcID- client_initial_secret `shouldBe` ClientTrafficSecret (dec16 "c00cf151ca5be075ed0ebfb5c80323c42d6b7db67881289af4008f1f6c357aea")+ client_initial_secret+ `shouldBe` ClientTrafficSecret+ (dec16 "c00cf151ca5be075ed0ebfb5c80323c42d6b7db67881289af4008f1f6c357aea") let ckey = aeadKey ver defaultCipher (Secret cis) ckey `shouldBe` Key (dec16 "1f369613dd76d5467730efcbe3b1a22d") let civ = initialVector ver defaultCipher (Secret cis)@@ -37,7 +39,9 @@ let chp = headerProtectionKey ver defaultCipher (Secret cis) chp `shouldBe` Key (dec16 "9f50449e04a0e810283a1e9933adedd2") let server_initial_secret@(ServerTrafficSecret sis) = serverInitialSecret ver dcID- server_initial_secret `shouldBe` ServerTrafficSecret (dec16 "3c199828fd139efd216c155ad844cc81fb82fa8d7446fa7d78be803acdda951b")+ server_initial_secret+ `shouldBe` ServerTrafficSecret+ (dec16 "3c199828fd139efd216c155ad844cc81fb82fa8d7446fa7d78be803acdda951b") let skey = aeadKey ver defaultCipher (Secret sis) skey `shouldBe` Key (dec16 "cf3a5331653c364c88f0f379b6067e37") let siv = initialVector ver defaultCipher (Secret sis)@@ -62,19 +66,20 @@ -- 00 -- scid len -- 00 -- token length -- 449e -- length: decodeInt (dec16 "449e")- -- 1182 = 4 + 1162 + 16 (fixme)+ -- 1182 = 4 + 1162 + 16 (fixme) -- 00000002 -- encoded packet number let bodyLen = fromIntegral $ decodeInt (dec16 "449e")- let padLen = bodyLen- - 4 -- packet number length- - 16 -- GCM encrypt expansion- - BS.length clientCRYPTOframe+ let padLen =+ bodyLen+ - 4 -- packet number length+ - 16 -- GCM encrypt expansion+ - BS.length clientCRYPTOframe clientCRYPTOframePadded = clientCRYPTOframe `BS.append` BS.pack (replicate padLen 0) let plaintext = clientCRYPTOframePadded let nonce = makeNonce civ $ dec16 "00000002" let add = AssDat clientPacketHeader- let (hdr,bdy) = fromJust $ niteEncrypt' defaultCipher ckey nonce plaintext add+ let (hdr, bdy) = fromJust $ niteEncrypt' defaultCipher ckey nonce plaintext add ciphertext = hdr `BS.append` bdy let plaintext' = fromJust $ niteDecrypt' defaultCipher ckey nonce ciphertext add plaintext' `shouldBe` plaintext@@ -106,15 +111,16 @@ -- 0001 -- encoded packet number let bodyLen = fromIntegral $ decodeInt (dec16 "4075")- let padLen = bodyLen- - 2 -- packet number length- - 16 -- GCM encrypt expansion- - BS.length serverCRYPTOframe+ let padLen =+ bodyLen+ - 2 -- packet number length+ - 16 -- GCM encrypt expansion+ - BS.length serverCRYPTOframe serverCRYPTOframePadded = serverCRYPTOframe `BS.append` BS.pack (replicate padLen 0) let plaintext = serverCRYPTOframePadded let nonce = makeNonce siv $ dec16 "0001" let add = AssDat serverPacketHeader- let (hdr,bdy) = fromJust $ niteEncrypt' defaultCipher skey nonce plaintext add+ let (hdr, bdy) = fromJust $ niteEncrypt' defaultCipher skey nonce plaintext add ciphertext = hdr `BS.append` bdy let plaintext' = fromJust $ niteDecrypt' defaultCipher skey nonce ciphertext add plaintext' `shouldBe` plaintext@@ -127,17 +133,18 @@ BS.take 5 mask `shouldBe` dec16 "2ec0d8356a" it "describes the examples of Retry" $ do- let wire0 = dec16 "ff000000010008f067a5502a4262b5746f6b656e04a265ba2eff4d829058fb3f0f2496ba"- (ipkt,rest) <- decodePacket wire0+ let wire0 =+ dec16 "ff000000010008f067a5502a4262b5746f6b656e04a265ba2eff4d829058fb3f0f2496ba"+ (ipkt, rest) <- decodePacket wire0 rest `shouldBe` "" case ipkt of- PacketIR retrypkt -> do- wire1 <- encodeRetryPacket retrypkt- let (f0,r0) = fromJust $ BS.uncons wire0- (f1,r1) = fromJust $ BS.uncons wire1- f0 .&. 0xf0 `shouldBe` f1 .&. 0xf0- r0 `shouldBe` r1- _ -> error "Retry version 1"+ PacketIR retrypkt -> do+ wire1 <- encodeRetryPacket retrypkt+ let (f0, r0) = fromJust $ BS.uncons wire0+ (f1, r1) = fromJust $ BS.uncons wire1+ f0 .&. 0xf0 `shouldBe` f1 .&. 0xf0+ r0 `shouldBe` r1+ _ -> error "Retry version 1" describe "test vector for version 2" $ do let ver = Version2@@ -146,7 +153,9 @@ -- shared keys let dcID = makeCID (dec16s "8394c8f03e515708") let client_initial_secret@(ClientTrafficSecret cis) = clientInitialSecret ver dcID- client_initial_secret `shouldBe` ClientTrafficSecret (dec16 "14ec9d6eb9fd7af83bf5a668bc17a7e283766aade7ecd0891f70f9ff7f4bf47b")+ client_initial_secret+ `shouldBe` ClientTrafficSecret+ (dec16 "14ec9d6eb9fd7af83bf5a668bc17a7e283766aade7ecd0891f70f9ff7f4bf47b") let ckey = aeadKey ver defaultCipher (Secret cis) ckey `shouldBe` Key (dec16 "8b1a0bc121284290a29e0971b5cd045d") let civ = initialVector ver defaultCipher (Secret cis)@@ -154,7 +163,9 @@ let chp = headerProtectionKey ver defaultCipher (Secret cis) chp `shouldBe` Key (dec16 "45b95e15235d6f45a6b19cbcb0294ba9") let server_initial_secret@(ServerTrafficSecret sis) = serverInitialSecret ver dcID- server_initial_secret `shouldBe` ServerTrafficSecret (dec16 "0263db1782731bf4588e7e4d93b7463907cb8cd8200b5da55a8bd488eafc37c1")+ server_initial_secret+ `shouldBe` ServerTrafficSecret+ (dec16 "0263db1782731bf4588e7e4d93b7463907cb8cd8200b5da55a8bd488eafc37c1") let skey = aeadKey ver defaultCipher (Secret sis) skey `shouldBe` Key (dec16 "82db637861d55e1d011f19ea71d5d2a7") let siv = initialVector ver defaultCipher (Secret sis)@@ -179,19 +190,20 @@ -- 00 -- scid len -- 00 -- token length -- 449e -- length: decodeInt (dec16 "449e")- -- 1182 = 4 + 1162 + 16 (fixme)+ -- 1182 = 4 + 1162 + 16 (fixme) -- 00000002 -- encoded packet number let bodyLen = fromIntegral $ decodeInt (dec16 "449e")- let padLen = bodyLen- - 4 -- packet number length- - 16 -- GCM encrypt expansion- - BS.length clientCRYPTOframe+ let padLen =+ bodyLen+ - 4 -- packet number length+ - 16 -- GCM encrypt expansion+ - BS.length clientCRYPTOframe clientCRYPTOframePadded = clientCRYPTOframe `BS.append` BS.pack (replicate padLen 0) let plaintext = clientCRYPTOframePadded let nonce = makeNonce civ $ dec16 "00000002" let add = AssDat clientPacketHeader- let (hdr,bdy) = fromJust $ niteEncrypt' defaultCipher ckey nonce plaintext add+ let (hdr, bdy) = fromJust $ niteEncrypt' defaultCipher ckey nonce plaintext add ciphertext = hdr `BS.append` bdy let plaintext' = fromJust $ niteDecrypt' defaultCipher ckey nonce ciphertext add plaintext' `shouldBe` plaintext@@ -223,15 +235,16 @@ -- 0001 -- encoded packet number let bodyLen = fromIntegral $ decodeInt (dec16 "4075")- let padLen = bodyLen- - 2 -- packet number length- - 16 -- GCM encrypt expansion- - BS.length serverCRYPTOframe+ let padLen =+ bodyLen+ - 2 -- packet number length+ - 16 -- GCM encrypt expansion+ - BS.length serverCRYPTOframe serverCRYPTOframePadded = serverCRYPTOframe `BS.append` BS.pack (replicate padLen 0) let plaintext = serverCRYPTOframePadded let nonce = makeNonce siv $ dec16 "0001" let add = AssDat serverPacketHeader- let (hdr,bdy) = fromJust $ niteEncrypt' defaultCipher skey nonce plaintext add+ let (hdr, bdy) = fromJust $ niteEncrypt' defaultCipher skey nonce plaintext add ciphertext = hdr `BS.append` bdy let plaintext' = fromJust $ niteDecrypt' defaultCipher skey nonce ciphertext add plaintext' `shouldBe` plaintext@@ -244,35 +257,39 @@ BS.take 5 mask `shouldBe` dec16 "4dd92e91ea" it "describes the examples of Retry" $ do- let wire0 = dec16 "cf6b3343cf0008f067a5502a4262b5746f6b656ec8646ce8bfe33952d955543665dcc7b6"- (ipkt,rest) <- decodePacket wire0+ let wire0 =+ dec16 "cf6b3343cf0008f067a5502a4262b5746f6b656ec8646ce8bfe33952d955543665dcc7b6"+ (ipkt, rest) <- decodePacket wire0 rest `shouldBe` "" case ipkt of- PacketIR retrypkt -> do- wire1 <- encodeRetryPacket retrypkt- let (f0,r0) = fromJust $ BS.uncons wire0- (f1,r1) = fromJust $ BS.uncons wire1- f0 .&. 0xf0 `shouldBe` f1 .&. 0xf0- r0 `shouldBe` r1- _ -> error "Retry version 2"-+ PacketIR retrypkt -> do+ wire1 <- encodeRetryPacket retrypkt+ let (f0, r0) = fromJust $ BS.uncons wire0+ (f1, r1) = fromJust $ BS.uncons wire1+ f0 .&. 0xf0 `shouldBe` f1 .&. 0xf0+ r0 `shouldBe` r1+ _ -> error "Retry version 2" serverCRYPTOframe :: BS.ByteString-serverCRYPTOframe = dec16 $ BS.concat [- "02000000000600405a020000560303eefce7f7b37ba1d1632e96677825ddf739"- , "88cfc79825df566dc5430b9a045a1200130100002e00330024001d00209d3c94"- , "0d89690b84d08a60993c144eca684d1081287c834d5311bcf32bb9da1a002b00"- , "020304"- ]+serverCRYPTOframe =+ dec16 $+ BS.concat+ [ "02000000000600405a020000560303eefce7f7b37ba1d1632e96677825ddf739"+ , "88cfc79825df566dc5430b9a045a1200130100002e00330024001d00209d3c94"+ , "0d89690b84d08a60993c144eca684d1081287c834d5311bcf32bb9da1a002b00"+ , "020304"+ ] clientCRYPTOframe :: BS.ByteString-clientCRYPTOframe = dec16 $ BS.concat [- "060040f1010000ed0303ebf8fa56f12939b9584a3896472ec40bb863cfd3e868"- , "04fe3a47f06a2b69484c00000413011302010000c000000010000e00000b6578"- , "616d706c652e636f6dff01000100000a00080006001d00170018001000070005"- , "04616c706e000500050100000000003300260024001d00209370b2c9caa47fba"- , "baf4559fedba753de171fa71f50f1ce15d43e994ec74d748002b000302030400"- , "0d0010000e0403050306030203080408050806002d00020101001c0002400100"- , "3900320408ffffffffffffffff05048000ffff07048000ffff08011001048000"- , "75300901100f088394c8f03e51570806048000ffff"- ]+clientCRYPTOframe =+ dec16 $+ BS.concat+ [ "060040f1010000ed0303ebf8fa56f12939b9584a3896472ec40bb863cfd3e868"+ , "04fe3a47f06a2b69484c00000413011302010000c000000010000e00000b6578"+ , "616d706c652e636f6dff01000100000a00080006001d00170018001000070005"+ , "04616c706e000500050100000000003300260024001d00209370b2c9caa47fba"+ , "baf4559fedba753de171fa71f50f1ce15d43e994ec74d748002b000302030400"+ , "0d0010000e0403050306030203080408050806002d00020101001c0002400100"+ , "3900320408ffffffffffffffff05048000ffff07048000ffff08011001048000"+ , "75300901100f088394c8f03e51570806048000ffff"+ ]
test/TransportError.hs view
@@ -1,14 +1,14 @@ {-# LANGUAGE OverloadedStrings #-} module TransportError (- transportErrorSpec- ) where+ transportErrorSpec,+) where import Control.Monad import Data.ByteString () import qualified Data.ByteString as BS import qualified Network.TLS as TLS-import Network.TLS.QUIC (ExtensionRaw(..))+import Network.TLS.QUIC (ExtensionRaw (..)) import Test.Hspec import UnliftIO.Concurrent import UnliftIO.Timeout@@ -40,119 +40,184 @@ transportErrorSpec :: ClientConfig -> Millisecond -> SpecWith a transportErrorSpec cc0 ms = do describe "QUIC servers" $ do- it "MUST send FLOW_CONTROL_ERROR if a STREAM frame with a large offset is received [Transport 4.1]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated largeOffset- runCnoOp cc ms `shouldThrow` transportErrorsIn [FlowControlError]+ it+ "MUST send FLOW_CONTROL_ERROR if a STREAM frame with a large offset is received [Transport 4.1]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated largeOffset+ runCnoOp cc ms `shouldThrow` transportErrorsIn [FlowControlError] it "MUST send STREAM_LIMIT_ERROR if a stream ID exceeding the limit" $ \_ -> do let cc = addHook cc0 $ setOnPlainCreated largeStreamId runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamLimitError]- it "MUST send TRANSPORT_PARAMETER_ERROR if initial_source_connection_id is missing [Transport 7.3]" $ \_ -> do- let cc = addHook cc0 $ setOnTransportParametersCreated dropInitialSourceConnectionId- runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]- it "MUST send TRANSPORT_PARAMETER_ERROR if original_destination_connection_id is received [Transport 18.2]" $ \_ -> do- let cc = addHook cc0 $ setOnTransportParametersCreated setOriginalDestinationConnectionId- runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]- it "MUST send TRANSPORT_PARAMETER_ERROR if preferred_address, is received [Transport 18.2]" $ \_ -> do- let cc = addHook cc0 $ setOnTransportParametersCreated setPreferredAddress- runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]- it "MUST send TRANSPORT_PARAMETER_ERROR if retry_source_connection_id is received [Transport 18.2]" $ \_ -> do- let cc = addHook cc0 $ setOnTransportParametersCreated setRetrySourceConnectionId- runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]- it "MUST send TRANSPORT_PARAMETER_ERROR if stateless_reset_token is received [Transport 18.2]" $ \_ -> do- let cc = addHook cc0 $ setOnTransportParametersCreated setStatelessResetToken- runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]- it "MUST send TRANSPORT_PARAMETER_ERROR if max_udp_payload_size < 1200 [Transport 7.4 and 18.2]" $ \_ -> do- let cc = addHook cc0 $ setOnTransportParametersCreated setMaxUdpPayloadSize- runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]- it "MUST send TRANSPORT_PARAMETER_ERROR if ack_delay_exponen > 20 [Transport 7.4 and 18.2]" $ \_ -> do- let cc = addHook cc0 $ setOnTransportParametersCreated setAckDelayExponent- runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]- it "MUST send TRANSPORT_PARAMETER_ERROR if max_ack_delay >= 2^14 [Transport 7.4 and 18.2]" $ \_ -> do- let cc = addHook cc0 $ setOnTransportParametersCreated setMaxAckDelay- runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]- it "MUST send FRAME_ENCODING_ERROR if a frame of unknown type is received [Transport 12.4]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated unknownFrame- runCnoOp cc ms `shouldThrow` transportErrorsIn [FrameEncodingError]+ it+ "MUST send TRANSPORT_PARAMETER_ERROR if initial_source_connection_id is missing [Transport 7.3]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTransportParametersCreated dropInitialSourceConnectionId+ runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]+ it+ "MUST send TRANSPORT_PARAMETER_ERROR if original_destination_connection_id is received [Transport 18.2]"+ $ \_ -> do+ let cc =+ addHook cc0 $ setOnTransportParametersCreated setOriginalDestinationConnectionId+ runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]+ it+ "MUST send TRANSPORT_PARAMETER_ERROR if preferred_address, is received [Transport 18.2]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTransportParametersCreated setPreferredAddress+ runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]+ it+ "MUST send TRANSPORT_PARAMETER_ERROR if retry_source_connection_id is received [Transport 18.2]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTransportParametersCreated setRetrySourceConnectionId+ runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]+ it+ "MUST send TRANSPORT_PARAMETER_ERROR if stateless_reset_token is received [Transport 18.2]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTransportParametersCreated setStatelessResetToken+ runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]+ it+ "MUST send TRANSPORT_PARAMETER_ERROR if max_udp_payload_size < 1200 [Transport 7.4 and 18.2]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTransportParametersCreated setMaxUdpPayloadSize+ runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]+ it+ "MUST send TRANSPORT_PARAMETER_ERROR if ack_delay_exponen > 20 [Transport 7.4 and 18.2]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTransportParametersCreated setAckDelayExponent+ runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]+ it+ "MUST send TRANSPORT_PARAMETER_ERROR if max_ack_delay >= 2^14 [Transport 7.4 and 18.2]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTransportParametersCreated setMaxAckDelay+ runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]+ it+ "MUST send FRAME_ENCODING_ERROR if a frame of unknown type is received [Transport 12.4]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated unknownFrame+ runCnoOp cc ms `shouldThrow` transportErrorsIn [FrameEncodingError] it "MUST send PROTOCOL_VIOLATION on no frames [Transport 12.4]" $ \_ -> do let cc = addHook cc0 $ setOnPlainCreated noFrames runCnoOp cc ms `shouldThrow` transportError- it "MUST send PROTOCOL_VIOLATION if reserved bits in Handshake are non-zero [Transport 17.2]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated $ rrBits HandshakeLevel- runCnoOp cc ms `shouldThrow` transportError- it "MUST send PROTOCOL_VIOLATION if PATH_CHALLENGE in Handshake is received [Transport 17.2.4]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated handshakePathChallenge- runCnoOp cc ms `shouldThrow` transportError- it "MUST send PROTOCOL_VIOLATION if reserved bits in Short are non-zero [Transport 17.2]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated $ rrBits RTT1Level- runCnoOp cc ms `shouldThrow` transportError- it "MUST send STREAM_STATE_ERROR if RESET_STREAM is received for a send-only stream [Transport 19.4]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated resetStrm- runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]- it "MUST send STREAM_STATE_ERROR if STOP_SENDING is received for a non-existing stream [Transport 19.5]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated stopSending- runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]+ it+ "MUST send PROTOCOL_VIOLATION if reserved bits in Handshake are non-zero [Transport 17.2]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated $ rrBits HandshakeLevel+ runCnoOp cc ms `shouldThrow` transportError+ it+ "MUST send PROTOCOL_VIOLATION if PATH_CHALLENGE in Handshake is received [Transport 17.2.4]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated handshakePathChallenge+ runCnoOp cc ms `shouldThrow` transportError+ it+ "MUST send PROTOCOL_VIOLATION if reserved bits in Short are non-zero [Transport 17.2]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated $ rrBits RTT1Level+ runCnoOp cc ms `shouldThrow` transportError+ it+ "MUST send STREAM_STATE_ERROR if RESET_STREAM is received for a send-only stream [Transport 19.4]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated resetStrm+ runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]+ it+ "MUST send STREAM_STATE_ERROR if STOP_SENDING is received for a non-existing stream [Transport 19.5]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated stopSending+ runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError] it "MUST send PROTOCOL_VIOLATION if NEW_TOKEN is received [Transport 19.7]" $ \_ -> do let cc = addHook cc0 $ setOnPlainCreated newToken runCnoOp cc ms `shouldThrow` transportError- it "MUST send STREAM_STATE_ERROR if it receives a STREAM frame for a locally-initiated stream that has not yet been created [Transport 19.8]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated localInitiatedNotCreatedYet- runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]- it "MUST send STREAM_STATE_ERROR if it receives a STREAM frame for a send-only stream [Transport 19.8]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated sendOnlyStream- runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]- it "MUST send STREAM_STATE_ERROR if MAX_STREAM_DATA is received for a non-existing stream [Transport 19.10]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated maxStreamData- runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]- it "MUST send STREAM_STATE_ERROR if MAX_STREAM_DATA is received for a receive-only stream [Transport 19.10]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated maxStreamData2- runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]- it "MUST send FRAME_ENCODING_ERROR if invalid MAX_STREAMS is received [Transport 19.11]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated maxStreams'- runCnoOp cc ms `shouldThrow` transportErrorsIn [FrameEncodingError]- it "MUST send STREAM_LIMIT_ERROR or FRAME_ENCODING_ERROR if invalid STREAMS_BLOCKED is received [Transport 19.14]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated streamsBlocked- runCnoOp cc ms `shouldThrow` transportErrorsIn [FrameEncodingError,StreamLimitError]- it "MUST send FRAME_ENCODING_ERROR if NEW_CONNECTION_ID with invalid Retire_Prior_To is received [Transport 19.15]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated $ newConnectionID ncidLargeRPT- runCnoOp cc ms `shouldThrow` transportErrorsIn [FrameEncodingError]- it "MUST send FRAME_ENCODING_ERROR if NEW_CONNECTION_ID with 0-byte CID is received [Transport 19.15]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated $ newConnectionID ncidZeroCID- runCnoOp cc ms `shouldThrow` transportErrorsIn [FrameEncodingError]- it "MUST send PROTOCOL_VIOLATION if HANDSHAKE_DONE is received [Transport 19.20]" $ \_ -> do- let cc = addHook cc0 $ setOnPlainCreated handshakeDone- runCnoOp cc ms `shouldThrow` transportError- it "MUST send unexpected_message TLS alert if KeyUpdate in Handshake is received [TLS 6]" $ \_ -> do- let cc = addHook cc0 $ setOnTLSHandshakeCreated cryptoKeyUpdate- runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.UnexpectedMessage]- it "MUST send unexpected_message TLS alert if KeyUpdate in 1-RTT is received [TLS 6]" $ \_ -> do- let cc = addHook cc0 $ setOnTLSHandshakeCreated cryptoKeyUpdate2- runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.UnexpectedMessage]- it "MUST send no_application_protocol TLS alert if no application protocols are supported [TLS 8.1]" $ \_ -> do- let cc = cc0 { ccALPN = \_ -> return $ Just ["dummy"] }- runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.NoApplicationProtocol]- it "MUST send missing_extension TLS alert if the quic_transport_parameters extension does not included [TLS 8.2]" $ \_ -> do- let cc = addHook cc0 $ setOnTLSExtensionCreated (const [])- runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.MissingExtension]- it "MUST send missing_extension TLS alert if the quic_transport_parameters extension does not included [TLS 8.2]" $ \_ -> do- let f [ExtensionRaw _ v] = [ExtensionRaw 0xffa5 v]- f _ = error "f"- cc = addHook cc0 $ setOnTLSExtensionCreated f- runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.MissingExtension]- it "MUST send unexpected_message TLS alert if EndOfEarlyData is received [TLS 8.3]" $ \_ -> do- let cc = addHook cc0 $ setOnTLSHandshakeCreated cryptoEndOfEarlyData- runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.UnexpectedMessage]+ it+ "MUST send STREAM_STATE_ERROR if it receives a STREAM frame for a locally-initiated stream that has not yet been created [Transport 19.8]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated localInitiatedNotCreatedYet+ runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]+ it+ "MUST send STREAM_STATE_ERROR if it receives a STREAM frame for a send-only stream [Transport 19.8]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated sendOnlyStream+ runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]+ it+ "MUST send STREAM_STATE_ERROR if MAX_STREAM_DATA is received for a non-existing stream [Transport 19.10]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated maxStreamData+ runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]+ it+ "MUST send STREAM_STATE_ERROR if MAX_STREAM_DATA is received for a receive-only stream [Transport 19.10]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated maxStreamData2+ runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamStateError]+ it+ "MUST send FRAME_ENCODING_ERROR if invalid MAX_STREAMS is received [Transport 19.11]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated maxStreams'+ runCnoOp cc ms `shouldThrow` transportErrorsIn [FrameEncodingError]+ it+ "MUST send STREAM_LIMIT_ERROR or FRAME_ENCODING_ERROR if invalid STREAMS_BLOCKED is received [Transport 19.14]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated streamsBlocked+ runCnoOp cc ms+ `shouldThrow` transportErrorsIn [FrameEncodingError, StreamLimitError]+ it+ "MUST send FRAME_ENCODING_ERROR if NEW_CONNECTION_ID with invalid Retire_Prior_To is received [Transport 19.15]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated $ newConnectionID ncidLargeRPT+ runCnoOp cc ms `shouldThrow` transportErrorsIn [FrameEncodingError]+ it+ "MUST send FRAME_ENCODING_ERROR if NEW_CONNECTION_ID with 0-byte CID is received [Transport 19.15]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated $ newConnectionID ncidZeroCID+ runCnoOp cc ms `shouldThrow` transportErrorsIn [FrameEncodingError]+ it+ "MUST send PROTOCOL_VIOLATION if HANDSHAKE_DONE is received [Transport 19.20]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnPlainCreated handshakeDone+ runCnoOp cc ms `shouldThrow` transportError+ it+ "MUST send unexpected_message TLS alert if KeyUpdate in Handshake is received [TLS 6]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTLSHandshakeCreated cryptoKeyUpdate+ runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.UnexpectedMessage]+ it+ "MUST send unexpected_message TLS alert if KeyUpdate in 1-RTT is received [TLS 6]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTLSHandshakeCreated cryptoKeyUpdate2+ runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.UnexpectedMessage]+ it+ "MUST send no_application_protocol TLS alert if no application protocols are supported [TLS 8.1]"+ $ \_ -> do+ let cc = cc0{ccALPN = \_ -> return $ Just ["dummy"]}+ runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.NoApplicationProtocol]+ it+ "MUST send missing_extension TLS alert if the quic_transport_parameters extension does not included [TLS 8.2]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTLSExtensionCreated (const [])+ runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.MissingExtension]+ it+ "MUST send missing_extension TLS alert if the quic_transport_parameters extension does not included [TLS 8.2]"+ $ \_ -> do+ let f [ExtensionRaw _ v] = [ExtensionRaw 0xffa5 v]+ f _ = error "f"+ cc = addHook cc0 $ setOnTLSExtensionCreated f+ runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.MissingExtension]+ it+ "MUST send unexpected_message TLS alert if EndOfEarlyData is received [TLS 8.3]"+ $ \_ -> do+ let cc = addHook cc0 $ setOnTLSHandshakeCreated cryptoEndOfEarlyData+ runCnoOp cc ms `shouldThrow` cryptoErrorsIn [TLS.UnexpectedMessage] it "MUST send PROTOCOL_VIOLATION if CRYPTO in 0-RTT is received [TLS 8.3]" $ \_ -> do mres <- runC cc0 ms getResumptionInfo case mres of- Just res- | is0RTTPossible res -> do- let cc1 = addHook cc0 $ setOnTLSHandshakeCreated crypto0RTT- cc = cc1 { ccResumption = res- , ccUse0RTT = True- }- runCnoOp cc ms `shouldThrow` transportError- _ -> do- putStrLn "Warning: 0-RTT is not possible. Skipping this test. Use \"h3spec -s 0-RTT\" next time."+ Just res+ | is0RTTPossible res -> do+ let cc1 = addHook cc0 $ setOnTLSHandshakeCreated crypto0RTT+ cc =+ cc1+ { ccResumption = res+ , ccUse0RTT = True+ }+ runCnoOp cc ms `shouldThrow` transportError+ _ -> do+ putStrLn+ "Warning: 0-RTT is not possible. Skipping this test. Use \"h3spec -s 0-RTT\" next time." when (ccDebugLog cc0) $ print mres ----------------------------------------------------------------@@ -160,159 +225,187 @@ addHook :: ClientConfig -> (Hooks -> Hooks) -> ClientConfig addHook cc modify = cc' where- cc' = cc { ccHooks = modify $ ccHooks cc }+ cc' = cc{ccHooks = modify $ ccHooks cc} setOnPlainCreated :: (EncryptionLevel -> Plain -> Plain) -> Hooks -> Hooks-setOnPlainCreated f hooks = hooks { onPlainCreated = f }+setOnPlainCreated f hooks = hooks{onPlainCreated = f} setOnTransportParametersCreated :: (Parameters -> Parameters) -> Hooks -> Hooks-setOnTransportParametersCreated f hooks = hooks { onTransportParametersCreated = f }+setOnTransportParametersCreated f hooks = hooks{onTransportParametersCreated = f} setOnTLSExtensionCreated :: ([ExtensionRaw] -> [ExtensionRaw]) -> Hooks -> Hooks-setOnTLSExtensionCreated f params = params { onTLSExtensionCreated = f }+setOnTLSExtensionCreated f params = params{onTLSExtensionCreated = f} -setOnTLSHandshakeCreated :: ([(EncryptionLevel,CryptoData)] -> ([(EncryptionLevel,CryptoData)],Bool)) -> Hooks -> Hooks-setOnTLSHandshakeCreated f hooks = hooks { onTLSHandshakeCreated = f }+setOnTLSHandshakeCreated+ :: ([(EncryptionLevel, CryptoData)] -> ([(EncryptionLevel, CryptoData)], Bool))+ -> Hooks+ -> Hooks+setOnTLSHandshakeCreated f hooks = hooks{onTLSHandshakeCreated = f} ---------------------------------------------------------------- rrBits :: EncryptionLevel -> EncryptionLevel -> Plain -> Plain rrBits lvl0 lvl plain- | lvl0 == lvl = if plainPacketNumber plain /= 0 then- plain { plainFlags = Flags 0x08 }- else- plain- | otherwise = plain+ | lvl0 == lvl =+ if plainPacketNumber plain /= 0+ then plain{plainFlags = Flags 0x08}+ else plain+ | otherwise = plain dropInitialSourceConnectionId :: Parameters -> Parameters-dropInitialSourceConnectionId params = params { initialSourceConnectionId = Nothing }+dropInitialSourceConnectionId params = params{initialSourceConnectionId = Nothing} dummyCID :: Maybe CID dummyCID = Just $ toCID "DUMMY" setOriginalDestinationConnectionId :: Parameters -> Parameters-setOriginalDestinationConnectionId params = params { originalDestinationConnectionId = dummyCID }+setOriginalDestinationConnectionId params = params{originalDestinationConnectionId = dummyCID} setPreferredAddress :: Parameters -> Parameters-setPreferredAddress params = params { preferredAddress = Just prefAddr }+setPreferredAddress params = params{preferredAddress = Just prefAddr} where- prefAddr = BS.concat- [ "\x7f\x00\x00\x01"- , "\x01\xbb"- , "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"- , "\x00\x00"- , "\x08"- , "\x00\x01\x02\x03\x04\x05\x06\x07"- , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"- ]+ prefAddr =+ BS.concat+ [ "\x7f\x00\x00\x01"+ , "\x01\xbb"+ , "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ , "\x00\x00"+ , "\x08"+ , "\x00\x01\x02\x03\x04\x05\x06\x07"+ , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"+ ] setRetrySourceConnectionId :: Parameters -> Parameters-setRetrySourceConnectionId params = params { retrySourceConnectionId = dummyCID }+setRetrySourceConnectionId params = params{retrySourceConnectionId = dummyCID} setStatelessResetToken :: Parameters -> Parameters-setStatelessResetToken params = params { statelessResetToken = Just $ StatelessResetToken "DUMMY" }+setStatelessResetToken params = params{statelessResetToken = Just $ StatelessResetToken "DUMMY"} ---------------------------------------------------------------- setMaxUdpPayloadSize :: Parameters -> Parameters-setMaxUdpPayloadSize params = params { maxUdpPayloadSize = 1090 }+setMaxUdpPayloadSize params = params{maxUdpPayloadSize = 1090} setAckDelayExponent :: Parameters -> Parameters-setAckDelayExponent params = params { ackDelayExponent = 30 }+setAckDelayExponent params = params{ackDelayExponent = 30} setMaxAckDelay :: Parameters -> Parameters-setMaxAckDelay params = params { maxAckDelay = 2^(15 :: Int) }+setMaxAckDelay params = params{maxAckDelay = 2 ^ (15 :: Int)} ---------------------------------------------------------------- largeOffset :: EncryptionLevel -> Plain -> Plain largeOffset lvl plain- | lvl == RTT1Level = plain { plainFrames = fake : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level = plain{plainFrames = fake : plainFrames plain}+ | otherwise = plain where fake = StreamF 0 100000000 ["GET /\r\n"] True largeStreamId :: EncryptionLevel -> Plain -> Plain largeStreamId lvl plain- | lvl == RTT1Level = plain { plainFrames = fake : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level = plain{plainFrames = fake : plainFrames plain}+ | otherwise = plain where fake = StreamF 1000000000 0 ["GET /\r\n"] True unknownFrame :: EncryptionLevel -> Plain -> Plain unknownFrame lvl plain- | lvl == RTT1Level = plain { plainFrames = UnknownFrame 0x20 : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain{plainFrames = UnknownFrame 0x20 : plainFrames plain}+ | otherwise = plain handshakePathChallenge :: EncryptionLevel -> Plain -> Plain handshakePathChallenge lvl plain- | lvl == HandshakeLevel = plain { plainFrames = PathChallenge (PathData "01234567") : plainFrames plain }- | otherwise = plain+ | lvl == HandshakeLevel =+ plain{plainFrames = PathChallenge (PathData "01234567") : plainFrames plain}+ | otherwise = plain noFrames :: EncryptionLevel -> Plain -> Plain noFrames lvl plain- | lvl == RTT1Level = plain { plainFrames = [], plainMarks = set4bytesPN $ setNoPaddings $ plainMarks plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain+ { plainFrames = []+ , plainMarks = set4bytesPN $ setNoPaddings $ plainMarks plain+ }+ | otherwise = plain handshakeDone :: EncryptionLevel -> Plain -> Plain handshakeDone lvl plain- | lvl == RTT1Level = plain { plainFrames = HandshakeDone : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level = plain{plainFrames = HandshakeDone : plainFrames plain}+ | otherwise = plain newToken :: EncryptionLevel -> Plain -> Plain newToken lvl plain- | lvl == RTT1Level = plain { plainFrames = NewToken "DUMMY" : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain{plainFrames = NewToken "DUMMY" : plainFrames plain}+ | otherwise = plain localInitiatedNotCreatedYet :: EncryptionLevel -> Plain -> Plain localInitiatedNotCreatedYet lvl plain- | lvl == RTT1Level = plain { plainFrames = StreamF 1 0 [""] False : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain{plainFrames = StreamF 1 0 [""] False : plainFrames plain}+ | otherwise = plain sendOnlyStream :: EncryptionLevel -> Plain -> Plain sendOnlyStream lvl plain- | lvl == RTT1Level = plain { plainFrames = StreamF 3 0 [""] False : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain{plainFrames = StreamF 3 0 [""] False : plainFrames plain}+ | otherwise = plain resetStrm :: EncryptionLevel -> Plain -> Plain resetStrm lvl plain- | lvl == RTT1Level = plain { plainFrames = ResetStream 3 (ApplicationProtocolError 0) 0 : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain+ { plainFrames = ResetStream 3 (ApplicationProtocolError 0) 0 : plainFrames plain+ }+ | otherwise = plain stopSending :: EncryptionLevel -> Plain -> Plain stopSending lvl plain- | lvl == RTT1Level = plain { plainFrames = StopSending 101 (ApplicationProtocolError 0) : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain+ { plainFrames = StopSending 101 (ApplicationProtocolError 0) : plainFrames plain+ }+ | otherwise = plain maxStreamData :: EncryptionLevel -> Plain -> Plain maxStreamData lvl plain- | lvl == RTT1Level = plain { plainFrames = MaxStreamData 101 1000000 : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain{plainFrames = MaxStreamData 101 1000000 : plainFrames plain}+ | otherwise = plain maxStreamData2 :: EncryptionLevel -> Plain -> Plain maxStreamData2 lvl plain- | lvl == RTT1Level = plain { plainFrames = MaxStreamData 2 1000000 : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain{plainFrames = MaxStreamData 2 1000000 : plainFrames plain}+ | otherwise = plain maxStreams' :: EncryptionLevel -> Plain -> Plain maxStreams' lvl plain- | lvl == RTT1Level = plain { plainFrames = MaxStreams Bidirectional (2^(60 :: Int) + 1) : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain+ { plainFrames = MaxStreams Bidirectional (2 ^ (60 :: Int) + 1) : plainFrames plain+ }+ | otherwise = plain streamsBlocked :: EncryptionLevel -> Plain -> Plain streamsBlocked lvl plain- | lvl == RTT1Level = plain { plainFrames = StreamsBlocked Bidirectional (2^(60 :: Int) + 1) : plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level =+ plain+ { plainFrames =+ StreamsBlocked Bidirectional (2 ^ (60 :: Int) + 1) : plainFrames plain+ }+ | otherwise = plain newConnectionID :: (Frame -> Frame) -> EncryptionLevel -> Plain -> Plain newConnectionID f lvl plain- | lvl == RTT1Level = plain { plainFrames = map f $ plainFrames plain }- | otherwise = plain+ | lvl == RTT1Level = plain{plainFrames = map f $ plainFrames plain}+ | otherwise = plain ncidZeroCID :: Frame -> Frame ncidZeroCID (NewConnectionID cidinfo0 rpt) = NewConnectionID cidinfo rpt where- cidinfo = cidinfo0 { cidInfoCID = CID "" }+ cidinfo = cidinfo0{cidInfoCID = CID ""} ncidZeroCID frame = frame ncidLargeRPT :: Frame -> Frame@@ -321,22 +414,26 @@ ---------------------------------------------------------------- -cryptoKeyUpdate :: [(EncryptionLevel,CryptoData)] -> ([(EncryptionLevel,CryptoData)],Bool)-cryptoKeyUpdate [(HandshakeLevel,fin)] = ([(HandshakeLevel,BS.append fin "\x18\x00\x00\x01\x01")],False)-cryptoKeyUpdate lcs = (lcs,False)+cryptoKeyUpdate+ :: [(EncryptionLevel, CryptoData)] -> ([(EncryptionLevel, CryptoData)], Bool)+cryptoKeyUpdate [(HandshakeLevel, fin)] = ([(HandshakeLevel, BS.append fin "\x18\x00\x00\x01\x01")], False)+cryptoKeyUpdate lcs = (lcs, False) -cryptoKeyUpdate2 :: [(EncryptionLevel,CryptoData)] -> ([(EncryptionLevel,CryptoData)],Bool)+cryptoKeyUpdate2+ :: [(EncryptionLevel, CryptoData)] -> ([(EncryptionLevel, CryptoData)], Bool) -- [] is intentionally created in RTT1Level for h3spec-cryptoKeyUpdate2 [] = ([(RTT1Level,"\x18\x00\x00\x01\x01")],False)-cryptoKeyUpdate2 lcs = (lcs,False)+cryptoKeyUpdate2 [] = ([(RTT1Level, "\x18\x00\x00\x01\x01")], False)+cryptoKeyUpdate2 lcs = (lcs, False) -cryptoEndOfEarlyData :: [(EncryptionLevel,CryptoData)] -> ([(EncryptionLevel,CryptoData)],Bool)-cryptoEndOfEarlyData [(HandshakeLevel,fin)] = ([(HandshakeLevel,BS.append "\x05\x00\x00\x00" fin)],False)-cryptoEndOfEarlyData lcs = (lcs,False)+cryptoEndOfEarlyData+ :: [(EncryptionLevel, CryptoData)] -> ([(EncryptionLevel, CryptoData)], Bool)+cryptoEndOfEarlyData [(HandshakeLevel, fin)] = ([(HandshakeLevel, BS.append "\x05\x00\x00\x00" fin)], False)+cryptoEndOfEarlyData lcs = (lcs, False) -crypto0RTT :: [(EncryptionLevel,CryptoData)] -> ([(EncryptionLevel,CryptoData)],Bool)-crypto0RTT [(InitialLevel,ch)] = ([(InitialLevel,ch),(RTT0Level,"\x08\x00\x00\x02\x00\x00")],True)-crypto0RTT lcs = (lcs,False)+crypto0RTT+ :: [(EncryptionLevel, CryptoData)] -> ([(EncryptionLevel, CryptoData)], Bool)+crypto0RTT [(InitialLevel, ch)] = ([(InitialLevel, ch), (RTT0Level, "\x08\x00\x00\x02\x00\x00")], True)+crypto0RTT lcs = (lcs, False) ---------------------------------------------------------------- @@ -351,7 +448,7 @@ -- of specific error codes. transportErrorsIn :: [TransportError] -> QUICException -> Bool transportErrorsIn tes qe@(TransportErrorIsReceived te _) = (te `elem` tes) || transportError qe-transportErrorsIn _ _ = False+transportErrorsIn _ _ = False cryptoErrorX :: QUICException -> Bool cryptoErrorX (TransportErrorIsReceived te _) = te `elem` [cryptoError TLS.InternalError, cryptoError TLS.HandshakeFailure]@@ -365,4 +462,4 @@ -- information. cryptoErrorsIn :: [TLS.AlertDescription] -> QUICException -> Bool cryptoErrorsIn tes qe@(TransportErrorIsReceived te _) = (te `elem` map cryptoError tes) || cryptoErrorX qe-cryptoErrorsIn _ _ = False+cryptoErrorsIn _ _ = False
test/TypesSpec.hs view
@@ -10,6 +10,8 @@ spec = do describe "toAckInfo and fromAckInfo" $ do it "should be dual" $ property $ \xs -> do- let rs = nub . sort . map (getSmall . getNonNegative) . getNonEmpty $ (xs :: NonEmptyList (NonNegative (Small PacketNumber)))+ let rs =+ nub . sort . map (getSmall . getNonNegative) . getNonEmpty $+ (xs :: NonEmptyList (NonNegative (Small PacketNumber))) rs' = reverse rs fromAckInfo (toAckInfo rs') `shouldBe` rs
util/ClientX.hs view
@@ -13,13 +13,13 @@ import H3 import Network.QUIC -data Aux = Aux {- auxPath :: String- , auxAuthority :: String- , auxDebug :: String -> IO ()- , auxShow :: ByteString -> IO ()- , auxCheckClose :: IO Bool- }+data Aux = Aux+ { auxPath :: String+ , auxAuthority :: String+ , auxDebug :: String -> IO ()+ , auxShow :: ByteString -> IO ()+ , auxCheckClose :: IO Bool+ } type Cli = Aux -> Connection -> IO () @@ -49,13 +49,13 @@ s6 <- unidirectionalStream conn s10 <- unidirectionalStream conn -- 0: control, 4 settings- sendStream s2 (BS.pack [0,4,8,1,80,0,6,128,0,128,0])+ sendStream s2 (BS.pack [0, 4, 8, 1, 80, 0, 6, 128, 0, 128, 0]) -- 2: from encoder to decoder sendStream s6 (BS.pack [2]) -- 3: from decoder to encoder sendStream s10 (BS.pack [3]) loop n0 hdrblk- where+ where loop 0 _ = auxDebug "Connection finished" loop 1 hdrblk = do auxDebug "GET"@@ -74,13 +74,14 @@ consume :: Aux -> Stream -> IO () consume aux@Aux{..} s = do bs <- recvStream s 1024- if bs == "" then do- auxDebug "Fin received"- closeStream s- else do- auxShow bs- auxDebug $ show (BS.length bs) ++ " bytes received"- consume aux s+ if bs == ""+ then do+ auxDebug "Fin received"+ closeStream s+ else do+ auxShow bs+ auxDebug $ show (BS.length bs) ++ " bytes received"+ consume aux s clientPF :: Word64 -> Cli clientPF n Aux{..} conn = do@@ -92,9 +93,10 @@ where loop s = do bs <- recvStream s 1024- if bs == "" then do- auxDebug "Connection finished"- closeStream s- else do- auxShow bs- loop s+ if bs == ""+ then do+ auxDebug "Connection finished"+ closeStream s+ else do+ auxShow bs+ loop s
util/Common.hs view
@@ -1,10 +1,10 @@ {-# LANGUAGE OverloadedStrings #-} module Common (- getGroups- , getLogger- , makeProtos- ) where+ getGroups,+ getLogger,+ makeProtos,+) where import Data.Bits import Data.ByteString (ByteString)@@ -22,32 +22,32 @@ , ("ffdhe4096", FFDHE4096) , ("ffdhe6144", FFDHE6144) , ("ffdhe8192", FFDHE8192)- , ("p256", P256)- , ("p384", P384)- , ("p521", P521)- , ("x25519", X25519)- , ("x448", X448)+ , ("p256", P256)+ , ("p384", P384)+ , ("p521", P521)+ , ("x25519", X25519)+ , ("x448", X448) ] getGroups :: [Group] -> Maybe String -> [Group]-getGroups grps Nothing = grps-getGroups _ (Just gs) = mapMaybe (`lookup` namedGroups) $ split ',' gs+getGroups grps Nothing = grps+getGroups _ (Just gs) = mapMaybe (`lookup` namedGroups) $ split ',' gs split :: Char -> String -> [String] split _ "" = []-split c s = case break (c==) s of- ("",r) -> split c (tail r)- (s',"") -> [s']- (s',r) -> s' : split c (tail r)+split c s = case break (c ==) s of+ ("", r) -> split c (tail r)+ (s', "") -> [s']+ (s', r) -> s' : split c (tail r) getLogger :: Maybe FilePath -> (String -> IO ())-getLogger Nothing = \_ -> return ()+getLogger Nothing = \_ -> return () getLogger (Just file) = \msg -> appendFile file (msg ++ "\n") makeProtos :: Version -> (ByteString, ByteString)-makeProtos Version1 = ("h3","hq-interop")-makeProtos Version2 = ("h3","hq-interop")-makeProtos ver = (h3X,hqX)+makeProtos Version1 = ("h3", "hq-interop")+makeProtos Version2 = ("h3", "hq-interop")+makeProtos ver = (h3X, hqX) where verbs = C8.pack $ show $ fromVersion ver h3X = "h3-" `BS.append` verbs
util/H3.hs view
@@ -4,11 +4,11 @@ {-# LANGUAGE StrictData #-} module H3 (- qpackClient- , qpackServer- , taglen- , html- ) where+ qpackClient,+ qpackServer,+ taglen,+ html,+) where import Data.Bits import Data.ByteString (ByteString)@@ -25,37 +25,41 @@ qpackServer :: IO ByteString qpackServer = do let status = 0b11000000 .|. 25 -- :status: 200- ct = 0b11000000 .|. 52 -- content-type: text/html; charset=utf8+ ct = 0b11000000 .|. 52 -- content-type: text/html; charset=utf8 server <- encStr 92 name- return $ BS.concat [BS.pack[0,0,status,ct],server]+ return $ BS.concat [BS.pack [0, 0, status, ct], server] qpackClient :: String -> String -> IO ByteString qpackClient path authority = do let method = 0b11000000 .|. 17 -- :method: GET scheme = 0b11000000 .|. 22 -- :scheme: http- path' <- encStr 1 $ C8.pack path- auth <- encStr 0 $ C8.pack authority- ua <- encStr 95 name- return $ BS.concat [BS.pack [0,0,method,scheme]- ,path'- ,auth- ,ua]+ path' <- encStr 1 $ C8.pack path+ auth <- encStr 0 $ C8.pack authority+ ua <- encStr 95 name+ return $+ BS.concat+ [ BS.pack [0, 0, method, scheme]+ , path'+ , auth+ , ua+ ] encStr :: Int -> ByteString -> IO ByteString encStr idx val = do k <- setQpackTag 0b01010000 <$> encodeInteger 4 idx v <- encodeHuffman val vlen <- setQpackTag 0b10000000 <$> encodeInteger 7 (BS.length v)- return $ BS.concat [k,vlen,v]+ return $ BS.concat [k, vlen, v] setQpackTag :: Word8 -> ByteString -> ByteString setQpackTag tag bs = BS.cons (tag .|. BS.head bs) (BS.tail bs) taglen :: Word8 -> ByteString -> ByteString-taglen i bs = BS.concat [tag,len,bs]+taglen i bs = BS.concat [tag, len, bs] where tag = BS.singleton i len = encodeInt $ fromIntegral $ BS.length bs html :: ByteString-html = "<html><head><title>Welcome to QUIC in Haskell</title></head><body><p>Welcome to QUIC in Haskell. This server asks clients to retry if no token/retry_token is provided. HTTP 0.9, HTTP/3 and QPACK implementations are a toy and hard-coded. No path validation at this moment.</p></body></html>"+html =+ "<html><head><title>Welcome to QUIC in Haskell</title></head><body><p>Welcome to QUIC in Haskell. This server asks clients to retry if no token/retry_token is provided. HTTP 0.9, HTTP/3 and QPACK implementations are a toy and hard-coded. No path validation at this moment.</p></body></html>"
util/ServerX.hs view
@@ -12,9 +12,9 @@ import Network.ByteOrder import qualified UnliftIO.Exception as E +import H3 import Network.QUIC import Network.QUIC.Internal-import H3 serverHQ :: Connection -> IO () serverHQ conn = connDebugLog conn "Connection terminated" `onE` body@@ -28,20 +28,21 @@ closeStream s serverH3 :: Connection -> IO ()-serverH3 conn = connDebugLog conn "Connection terminated" `onE` do- s3 <- unidirectionalStream conn- s7 <- unidirectionalStream conn- s11 <- unidirectionalStream conn- -- 0: control, 4 settings- sendStream s3 (BS.pack [0,4,8,1,80,0,6,128,0,128,0])- -- 2: from encoder to decoder- sendStream s7 (BS.pack [2])- -- 3: from decoder to encoder- sendStream s11 (BS.pack [3])- hdrblock <- taglen 1 <$> qpackServer- let bdyblock = taglen 0 html- hdrbdy = [hdrblock,bdyblock]- loop hdrbdy+serverH3 conn =+ connDebugLog conn "Connection terminated" `onE` do+ s3 <- unidirectionalStream conn+ s7 <- unidirectionalStream conn+ s11 <- unidirectionalStream conn+ -- 0: control, 4 settings+ sendStream s3 (BS.pack [0, 4, 8, 1, 80, 0, 6, 128, 0, 128, 0])+ -- 2: from encoder to decoder+ sendStream s7 (BS.pack [2])+ -- 3: from decoder to encoder+ sendStream s11 (BS.pack [3])+ hdrblock <- taglen 1 <$> qpackServer+ let bdyblock = taglen 0 html+ hdrbdy = [hdrblock, bdyblock]+ loop hdrbdy where loop hdrbdy = do s <- acceptStream conn@@ -54,20 +55,21 @@ loop hdrbdy serverPF :: Connection -> IO ()-serverPF conn = connDebugLog conn "Connection terminated" `onE` do- s <- acceptStream conn- let sid = streamId s- when (isClientInitiatedBidirectional sid) $ do- bs <- recvStream s 8- n <- withReadBuffer bs read64- loop s n- closeStream s+serverPF conn =+ connDebugLog conn "Connection terminated" `onE` do+ s <- acceptStream conn+ let sid = streamId s+ when (isClientInitiatedBidirectional sid) $ do+ bs <- recvStream s 8+ n <- withReadBuffer bs read64+ loop s n+ closeStream s where bs1024 = BS.replicate 1024 65 loop _ 0 = return () loop s n- | n < 1024 = sendStream s $ BS.replicate (fromIntegral n) 65- | otherwise = do+ | n < 1024 = sendStream s $ BS.replicate (fromIntegral n) 65+ | otherwise = do sendStream s bs1024 loop s (n - 1024) @@ -76,11 +78,11 @@ where loop = do bs <- recvStream s 1024- if bs == "" then- connDebugLog conn "FIN received"- else do- connDebugLog conn $ byteString bs- loop+ if bs == ""+ then connDebugLog conn "FIN received"+ else do+ connDebugLog conn $ byteString bs+ loop onE :: IO b -> IO a -> IO a h `onE` b = b `E.onException` h
util/client.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE Strict #-}@@ -26,118 +27,162 @@ import Network.QUIC.Client import Network.QUIC.Internal hiding (RTT0) -data Options = Options {- optDebugLog :: Bool- , optShow :: Bool- , optQLogDir :: Maybe FilePath- , optKeyLogFile :: Maybe FilePath- , optGroups :: Maybe String- , optValidate :: Bool- , optHQ :: Bool- , optVerNego :: Bool- , optResumption :: Bool- , opt0RTT :: Bool- , optRetry :: Bool- , optQuantum :: Bool- , optInteractive :: Bool- , optMigration :: Maybe ConnectionControl- , optPacketSize :: Maybe Int- , optPerformance :: Word64- , optNumOfReqs :: Int- , optUnconSock :: Bool- } deriving Show+data Options = Options+ { optDebugLog :: Bool+ , optShow :: Bool+ , optQLogDir :: Maybe FilePath+ , optKeyLogFile :: Maybe FilePath+ , optGroups :: Maybe String+ , optValidate :: Bool+ , optHQ :: Bool+ , optVerNego :: Bool+ , optResumption :: Bool+ , opt0RTT :: Bool+ , optRetry :: Bool+ , optQuantum :: Bool+ , optInteractive :: Bool+ , optMigration :: Maybe ConnectionControl+ , optPacketSize :: Maybe Int+ , optPerformance :: Word64+ , optNumOfReqs :: Int+ , optUnconSock :: Bool+ }+ deriving (Show) defaultOptions :: Options-defaultOptions = Options {- optDebugLog = False- , optShow = False- , optQLogDir = Nothing- , optKeyLogFile = Nothing- , optGroups = Nothing- , optHQ = False- , optValidate = False- , optVerNego = False- , optResumption = False- , opt0RTT = False- , optRetry = False- , optQuantum = False- , optInteractive = False- , optMigration = Nothing- , optPacketSize = Nothing- , optPerformance = 0- , optNumOfReqs = 1- , optUnconSock = True- }+defaultOptions =+ Options+ { optDebugLog = False+ , optShow = False+ , optQLogDir = Nothing+ , optKeyLogFile = Nothing+ , optGroups = Nothing+ , optHQ = False+ , optValidate = False+ , optVerNego = False+ , optResumption = False+ , opt0RTT = False+ , optRetry = False+ , optQuantum = False+ , optInteractive = False+ , optMigration = Nothing+ , optPacketSize = Nothing+ , optPerformance = 0+ , optNumOfReqs = 1+ , optUnconSock = True+ } usage :: String usage = "Usage: client [OPTION] addr port" options :: [OptDescr (Options -> Options)]-options = [- Option ['d'] ["debug"]- (NoArg (\o -> o { optDebugLog = True }))- "print debug info"- , Option ['v'] ["show-content"]- (NoArg (\o -> o { optShow = True }))- "print downloaded content"- , Option ['q'] ["qlog-dir"]- (ReqArg (\dir o -> o { optQLogDir = Just dir }) "<dir>")- "directory to store qlog"- , Option ['l'] ["key-log-file"]- (ReqArg (\file o -> o { optKeyLogFile = Just file }) "<file>")- "a file to store negotiated secrets"- , Option ['g'] ["groups"]- (ReqArg (\gs o -> o { optGroups = Just gs }) "<groups>")- "specify groups"- , Option ['c'] ["validate"]- (NoArg (\o -> o { optValidate = True }))- "validate server's certificate"- , Option ['r'] ["hq"]- (NoArg (\o -> o { optHQ = True }))- "prefer hq (HTTP/0.9)"- , Option ['s'] ["packet-size"]- (ReqArg (\n o -> o { optPacketSize = Just (read n) }) "<size>")- "specify QUIC packet size (UDP payload size)"- , Option ['i'] ["interactive"]- (NoArg (\o -> o { optInteractive = True }))- "prefer hq (HTTP/0.9)"- , Option ['V'] ["vernego"]- (NoArg (\o -> o { optVerNego = True }))- "try version negotiation"- , Option ['R'] ["resumption"]- (NoArg (\o -> o { optResumption = True }))- "try session resumption"- , Option ['Z'] ["0rtt"]- (NoArg (\o -> o { opt0RTT = True }))- "try sending early data"- , Option ['S'] ["stateless-retry"]- (NoArg (\o -> o { optRetry = True }))- "check stateless retry"- , Option ['Q'] ["quantum"]- (NoArg (\o -> o { optQuantum = True }))- "try sending large Initials"- , Option ['M'] ["change-server-cid"]- (NoArg (\o -> o { optMigration = Just ChangeServerCID }))- "use a new server CID"- , Option ['N'] ["change-client-cid"]- (NoArg (\o -> o { optMigration = Just ChangeClientCID }))- "use a new client CID"- , Option ['B'] ["nat-rebinding"]- (NoArg (\o -> o { optMigration = Just NATRebinding }))- "use a new local port"- , Option ['A'] ["address-mobility"]- (NoArg (\o -> o { optMigration = Just ActiveMigration }))- "use a new address and a new server CID"- , Option ['t'] ["performance"]- (ReqArg (\n o -> o { optPerformance = read n }) "<size>")- "measure performance"- , Option ['n'] ["number-of-requests"]- (ReqArg (\n o -> o { optNumOfReqs = read n }) "<n>")- "specify the number of requests"- , Option ['m'] ["use-connected-socket"]- (NoArg (\o -> o { optUnconSock = False }))- "use connected sockets instead of unconnected sockets"- ]+options =+ [ Option+ ['d']+ ["debug"]+ (NoArg (\o -> o{optDebugLog = True}))+ "print debug info"+ , Option+ ['v']+ ["show-content"]+ (NoArg (\o -> o{optShow = True}))+ "print downloaded content"+ , Option+ ['q']+ ["qlog-dir"]+ (ReqArg (\dir o -> o{optQLogDir = Just dir}) "<dir>")+ "directory to store qlog"+ , Option+ ['l']+ ["key-log-file"]+ (ReqArg (\file o -> o{optKeyLogFile = Just file}) "<file>")+ "a file to store negotiated secrets"+ , Option+ ['g']+ ["groups"]+ (ReqArg (\gs o -> o{optGroups = Just gs}) "<groups>")+ "specify groups"+ , Option+ ['c']+ ["validate"]+ (NoArg (\o -> o{optValidate = True}))+ "validate server's certificate"+ , Option+ ['r']+ ["hq"]+ (NoArg (\o -> o{optHQ = True}))+ "prefer hq (HTTP/0.9)"+ , Option+ ['s']+ ["packet-size"]+ (ReqArg (\n o -> o{optPacketSize = Just (read n)}) "<size>")+ "specify QUIC packet size (UDP payload size)"+ , Option+ ['i']+ ["interactive"]+ (NoArg (\o -> o{optInteractive = True}))+ "prefer hq (HTTP/0.9)"+ , Option+ ['V']+ ["vernego"]+ (NoArg (\o -> o{optVerNego = True}))+ "try version negotiation"+ , Option+ ['R']+ ["resumption"]+ (NoArg (\o -> o{optResumption = True}))+ "try session resumption"+ , Option+ ['Z']+ ["0rtt"]+ (NoArg (\o -> o{opt0RTT = True}))+ "try sending early data"+ , Option+ ['S']+ ["stateless-retry"]+ (NoArg (\o -> o{optRetry = True}))+ "check stateless retry"+ , Option+ ['Q']+ ["quantum"]+ (NoArg (\o -> o{optQuantum = True}))+ "try sending large Initials"+ , Option+ ['M']+ ["change-server-cid"]+ (NoArg (\o -> o{optMigration = Just ChangeServerCID}))+ "use a new server CID"+ , Option+ ['N']+ ["change-client-cid"]+ (NoArg (\o -> o{optMigration = Just ChangeClientCID}))+ "use a new client CID"+ , Option+ ['B']+ ["nat-rebinding"]+ (NoArg (\o -> o{optMigration = Just NATRebinding}))+ "use a new local port"+ , Option+ ['A']+ ["address-mobility"]+ (NoArg (\o -> o{optMigration = Just ActiveMigration}))+ "use a new address and a new server CID"+ , Option+ ['t']+ ["performance"]+ (ReqArg (\n o -> o{optPerformance = read n}) "<size>")+ "measure performance"+ , Option+ ['n']+ ["number-of-requests"]+ (ReqArg (\n o -> o{optNumOfReqs = read n}) "<n>")+ "specify the number of requests"+ , Option+ ['m']+ ["use-connected-socket"]+ (NoArg (\o -> o{optUnconSock = False}))+ "use connected sockets instead of unconnected sockets"+ ] showUsageAndExit :: String -> IO a showUsageAndExit msg = do@@ -148,8 +193,8 @@ clientOpts :: [String] -> IO (Options, [String]) clientOpts argv = case getOpt Permute options argv of- (o,n,[] ) -> return (foldl (flip id) defaultOptions o, n)- (_,_,errs) -> showUsageAndExit $ concat errs+ (o, n, []) -> return (foldl (flip id) defaultOptions o, n)+ (_, _, errs) -> showUsageAndExit $ concat errs main :: IO () main = do@@ -159,78 +204,89 @@ when (ipslen /= 2 && ipslen /= 3) $ showUsageAndExit "cannot recognize <addr> and <port>\n" cmvar <- newEmptyMVar- let path | ipslen == 3 = ips !! 2- | otherwise = "/"+ let path+ | ipslen == 3 = ips !! 2+ | otherwise = "/" addr = head ips port = head $ tail ips ccalpn ver- | optPerformance /= 0 = return $ Just ["perf"]- | otherwise = let (h3X, hqX) = makeProtos ver- protos | optHQ = [hqX,h3X]- | otherwise = [h3X,hqX]- in return $ Just protos+ | optPerformance /= 0 = return $ Just ["perf"]+ | otherwise =+ let (h3X, hqX) = makeProtos ver+ protos+ | optHQ = [hqX, h3X]+ | otherwise = [h3X, hqX]+ in return $ Just protos gvers vers- | optVerNego = GreasingVersion : vers- | otherwise = vers+ | optVerNego = GreasingVersion : vers+ | otherwise = vers setTPQuantum params- | optQuantum = let bs = BS.replicate 1200 0- in params { grease = Just bs }- | otherwise = params+ | optQuantum =+ let bs = BS.replicate 1200 0+ in params{grease = Just bs}+ | otherwise = params cc0 = defaultClientConfig- cc = cc0 {- ccServerName = addr- , ccPortName = port- , ccALPN = ccalpn- , ccValidate = optValidate- , ccPacketSize = optPacketSize- , ccDebugLog = optDebugLog- , ccVersions = gvers $ ccVersions cc0- , ccParameters = setTPQuantum $ ccParameters cc0- , ccKeyLog = getLogger optKeyLogFile- , ccGroups = getGroups (ccGroups cc0) optGroups- , ccQLog = optQLogDir- , ccHooks = defaultHooks {- onCloseCompleted = putMVar cmvar ()- }- , ccAutoMigration = optUnconSock- }- debug | optDebugLog = putStrLn- | otherwise = \_ -> return ()- showContent | optShow = C8.putStrLn- | otherwise = \_ -> return ()- aux = Aux {- auxPath = path- , auxAuthority = addr- , auxDebug = debug- , auxShow = showContent- , auxCheckClose = do- mx <- T.timeout 1000000 $ takeMVar cmvar- case mx of- Nothing -> return False- _ -> return True- }+ cc =+ cc0+ { ccServerName = addr+ , ccPortName = port+ , ccALPN = ccalpn+ , ccValidate = optValidate+ , ccPacketSize = optPacketSize+ , ccDebugLog = optDebugLog+ , ccVersions = gvers $ ccVersions cc0+ , ccParameters = setTPQuantum $ ccParameters cc0+ , ccKeyLog = getLogger optKeyLogFile+ , ccGroups = getGroups (ccGroups cc0) optGroups+ , ccQLog = optQLogDir+ , ccHooks =+ defaultHooks+ { onCloseCompleted = putMVar cmvar ()+ }+ , ccAutoMigration = optUnconSock+ }+ debug+ | optDebugLog = putStrLn+ | otherwise = \_ -> return ()+ showContent+ | optShow = C8.putStrLn+ | otherwise = \_ -> return ()+ aux =+ Aux+ { auxPath = path+ , auxAuthority = addr+ , auxDebug = debug+ , auxShow = showContent+ , auxCheckClose = do+ mx <- T.timeout 1000000 $ takeMVar cmvar+ case mx of+ Nothing -> return False+ _ -> return True+ } runClient cc opts aux runClient :: ClientConfig -> Options -> Aux -> IO () runClient cc opts@Options{..} aux@Aux{..} = do auxDebug "------------------------"- (info1,info2,res,mig,client') <- run cc $ \conn -> do+ (info1, info2, res, mig, client') <- run cc $ \conn -> do i1 <- getConnectionInfo conn let client = case alpn i1 of- Just proto | "hq" `BS.isPrefixOf` proto -> clientHQ optNumOfReqs- | "h3" `BS.isPrefixOf` proto -> clientH3 optNumOfReqs- _ -> clientPF optPerformance+ Just proto+ | "hq" `BS.isPrefixOf` proto -> clientHQ optNumOfReqs+ | "h3" `BS.isPrefixOf` proto -> clientH3 optNumOfReqs+ _ -> clientPF optPerformance m <- case optMigration of- Nothing -> return False- Just mtyp -> do- x <- controlConnection conn mtyp- auxDebug $ "Migration by " ++ show mtyp- return x+ Nothing -> return False+ Just mtyp -> do+ x <- controlConnection conn mtyp+ auxDebug $ "Migration by " ++ show mtyp+ return x t1 <- getUnixTime- if optInteractive then do- console aux client conn- else do- client aux conn+ if optInteractive+ then do+ console aux client conn+ else do+ client aux conn stats <- getConnectionStats conn print stats t2 <- getUnixTime@@ -238,84 +294,99 @@ r <- getResumptionInfo conn printThroughput t1 t2 stats return (i1, i2, r, m, client)- if optVerNego then do- putStrLn "Result: (V) version negotiation ... OK"- exitSuccess- else if optQuantum then do- putStrLn "Result: (Q) quantum ... OK"- exitSuccess- else if optResumption then do- if isResumptionPossible res then do- info3 <- runClient2 cc opts aux res client'- if handshakeMode info3 == PreSharedKey then do- putStrLn "Result: (R) TLS resumption ... OK"+ if+ | optVerNego -> do+ putStrLn "Result: (V) version negotiation ... OK"+ exitSuccess+ | optQuantum -> do+ putStrLn "Result: (Q) quantum ... OK"+ exitSuccess+ | optResumption -> do+ if isResumptionPossible res+ then do+ info3 <- runClient2 cc opts aux res client'+ if handshakeMode info3 == PreSharedKey+ then do+ putStrLn "Result: (R) TLS resumption ... OK"+ exitSuccess+ else do+ putStrLn "Result: (R) TLS resumption ... NG"+ exitFailure+ else do+ putStrLn "Result: (R) TLS resumption ... NG"+ exitFailure+ | opt0RTT -> do+ if is0RTTPossible res+ then do+ info3 <- runClient2 cc opts aux res client'+ if handshakeMode info3 == RTT0+ then do+ putStrLn "Result: (Z) 0-RTT ... OK"+ exitSuccess+ else do+ putStrLn "Result: (Z) 0-RTT ... NG"+ exitFailure+ else do+ putStrLn "Result: (Z) 0-RTT ... NG"+ exitFailure+ | optRetry -> do+ if retry info1+ then do+ putStrLn "Result: (S) retry ... OK"+ exitSuccess+ else do+ putStrLn "Result: (S) retry ... NG"+ exitFailure+ | otherwise -> case optMigration of+ Just ChangeServerCID -> do+ let changed = remoteCID info1 /= remoteCID info2+ if mig && remoteCID info1 /= remoteCID info2+ then do+ putStrLn "Result: (M) change server CID ... OK"+ exitSuccess+ else do+ putStrLn $ "Result: (M) change server CID ... NG " ++ show (mig, changed)+ exitFailure+ Just ChangeClientCID -> do+ let changed = localCID info1 /= localCID info2+ if mig && changed+ then do+ putStrLn "Result: (N) change client CID ... OK"+ exitSuccess+ else do+ putStrLn $ "Result: (N) change client CID ... NG " ++ show (mig, changed)+ exitFailure+ Just NATRebinding -> do+ putStrLn "Result: (B) NAT rebinding ... OK" exitSuccess- else do- putStrLn "Result: (R) TLS resumption ... NG"- exitFailure- else do- putStrLn "Result: (R) TLS resumption ... NG"- exitFailure- else if opt0RTT then do- if is0RTTPossible res then do- info3 <- runClient2 cc opts aux res client'- if handshakeMode info3 == RTT0 then do- putStrLn "Result: (Z) 0-RTT ... OK"+ Just ActiveMigration -> do+ let changed = remoteCID info1 /= remoteCID info2+ if mig && changed+ then do+ putStrLn "Result: (A) address mobility ... OK"+ exitSuccess+ else do+ putStrLn $ "Result: (A) address mobility ... NG " ++ show (mig, changed)+ exitFailure+ Nothing -> do+ putStrLn "Result: (H) handshake ... OK"+ putStrLn "Result: (D) stream data ... OK"+ closeCompleted <- auxCheckClose+ when closeCompleted $ putStrLn "Result: (C) close completed ... OK"+ case alpn info1 of+ Nothing -> return ()+ Just alpn ->+ when ("h3" `BS.isPrefixOf` alpn) $+ putStrLn "Result: (3) H3 transaction ... OK" exitSuccess- else do- putStrLn "Result: (Z) 0-RTT ... NG"- exitFailure- else do- putStrLn "Result: (Z) 0-RTT ... NG"- exitFailure- else if optRetry then do- if retry info1 then do- putStrLn "Result: (S) retry ... OK"- exitSuccess- else do- putStrLn "Result: (S) retry ... NG"- exitFailure- else case optMigration of- Just ChangeServerCID -> do- let changed = remoteCID info1 /= remoteCID info2- if mig && remoteCID info1 /= remoteCID info2 then do- putStrLn "Result: (M) change server CID ... OK"- exitSuccess- else do- putStrLn $ "Result: (M) change server CID ... NG " ++ show (mig,changed)- exitFailure- Just ChangeClientCID -> do- let changed = localCID info1 /= localCID info2- if mig && changed then do- putStrLn "Result: (N) change client CID ... OK"- exitSuccess- else do- putStrLn $ "Result: (N) change client CID ... NG " ++ show (mig,changed)- exitFailure- Just NATRebinding -> do- putStrLn "Result: (B) NAT rebinding ... OK"- exitSuccess- Just ActiveMigration -> do- let changed = remoteCID info1 /= remoteCID info2- if mig && changed then do- putStrLn "Result: (A) address mobility ... OK"- exitSuccess- else do- putStrLn $ "Result: (A) address mobility ... NG " ++ show (mig,changed)- exitFailure- Nothing -> do- putStrLn "Result: (H) handshake ... OK"- putStrLn "Result: (D) stream data ... OK"- closeCompleted <- auxCheckClose- when closeCompleted $ putStrLn "Result: (C) close completed ... OK"- case alpn info1 of- Nothing -> return ()- Just alpn -> when ("h3" `BS.isPrefixOf` alpn) $- putStrLn "Result: (3) H3 transaction ... OK"- exitSuccess -runClient2 :: ClientConfig -> Options -> Aux -> ResumptionInfo -> Cli- -> IO ConnectionInfo+runClient2+ :: ClientConfig+ -> Options+ -> Aux+ -> ResumptionInfo+ -> Cli+ -> IO ConnectionInfo runClient2 cc Options{..} aux@Aux{..} res client = do threadDelay 100000 auxDebug "<<<< next connection >>>>"@@ -324,20 +395,31 @@ void $ client aux conn getConnectionInfo conn where- cc' = cc {- ccResumption = res- , ccUse0RTT = opt0RTT && is0RTTPossible res- }+ cc' =+ cc+ { ccResumption = res+ , ccUse0RTT = opt0RTT && is0RTTPossible res+ } printThroughput :: UnixTime -> UnixTime -> ConnectionStats -> IO () printThroughput t1 t2 ConnectionStats{..} =- printf "Throughput %.2f Mbps (%d bytes in %d msecs)\n" bytesPerSeconds rxBytes millisecs+ printf+ "Throughput %.2f Mbps (%d bytes in %d msecs)\n"+ bytesPerSeconds+ rxBytes+ millisecs where UnixDiffTime (CTime s) u = t2 `diffUnixTime` t1 millisecs :: Int millisecs = fromIntegral s * 1000 + fromIntegral u `div` 1000 bytesPerSeconds :: Double- bytesPerSeconds = fromIntegral rxBytes * (1000 :: Double) * 8 / fromIntegral millisecs / 1024 / 1024+ bytesPerSeconds =+ fromIntegral rxBytes+ * (1000 :: Double)+ * 8+ / fromIntegral millisecs+ / 1024+ / 1024 console :: Aux -> (Aux -> Connection -> IO ()) -> Connection -> IO () console aux client conn = do@@ -347,25 +429,25 @@ putStrLn "p -- ping" putStrLn "n -- NAT rebinding" loop- where- loop = do- hSetBuffering stdout NoBuffering- putStr "> "- hSetBuffering stdout LineBuffering- l <- getLine- case l of- "q" -> putStrLn "bye"- "g" -> do- putStrLn $ "GET " ++ auxPath aux- _ <- forkIO $ client aux conn- loop- "p" -> do- putStrLn "Ping"- sendFrames conn RTT1Level [Ping]- loop- "n" -> do- controlConnection conn NATRebinding >>= print- loop- _ -> do- putStrLn "No such command"- loop+ where+ loop = do+ hSetBuffering stdout NoBuffering+ putStr "> "+ hSetBuffering stdout LineBuffering+ l <- getLine+ case l of+ "q" -> putStrLn "bye"+ "g" -> do+ putStrLn $ "GET " ++ auxPath aux+ _ <- forkIO $ client aux conn+ loop+ "p" -> do+ putStrLn "Ping"+ sendFrames conn RTT1Level [Ping]+ loop+ "n" -> do+ controlConnection conn NATRebinding >>= print+ loop+ _ -> do+ putStrLn "No such command"+ loop
util/server.hs view
@@ -11,7 +11,7 @@ import Data.ByteString (ByteString) import qualified Data.ByteString as BS import qualified Data.List as L-import Network.TLS (credentialLoadX509, Credentials(..))+import Network.TLS (Credentials (..), credentialLoadX509) import qualified Network.TLS.SessionManager as SM import System.Console.GetOpt import System.Environment (getArgs)@@ -24,51 +24,67 @@ import Network.QUIC.Server import ServerX -data Options = Options {- optDebugLogDir :: Maybe FilePath- , optQLogDir :: Maybe FilePath- , optKeyLogFile :: Maybe FilePath- , optGroups :: Maybe String- , optCertFile :: FilePath- , optKeyFile :: FilePath- , optRetry :: Bool- } deriving Show+data Options = Options+ { optDebugLogDir :: Maybe FilePath+ , optQLogDir :: Maybe FilePath+ , optKeyLogFile :: Maybe FilePath+ , optGroups :: Maybe String+ , optCertFile :: FilePath+ , optKeyFile :: FilePath+ , optRetry :: Bool+ }+ deriving (Show) defaultOptions :: Options-defaultOptions = Options {- optDebugLogDir = Nothing- , optQLogDir = Nothing- , optKeyLogFile = Nothing- , optGroups = Nothing- , optCertFile = "servercert.pem"- , optKeyFile = "serverkey.pem"- , optRetry = False- }+defaultOptions =+ Options+ { optDebugLogDir = Nothing+ , optQLogDir = Nothing+ , optKeyLogFile = Nothing+ , optGroups = Nothing+ , optCertFile = "servercert.pem"+ , optKeyFile = "serverkey.pem"+ , optRetry = False+ } options :: [OptDescr (Options -> Options)]-options = [- Option ['d'] ["debug-log-dir"]- (ReqArg (\dir o -> o { optDebugLogDir = Just dir }) "<dir>")- "directory to store a debug file"- , Option ['q'] ["qlog-dir"]- (ReqArg (\dir o -> o { optQLogDir = Just dir }) "<dir>")- "directory to store qlog"- , Option ['l'] ["key-log-file"]- (ReqArg (\file o -> o { optKeyLogFile = Just file }) "<file>")- "a file to store negotiated secrets"- , Option ['g'] ["groups"]- (ReqArg (\gs o -> o { optGroups = Just gs }) "<groups>")- "groups for key exchange"- , Option ['c'] ["cert"]- (ReqArg (\fl o -> o { optCertFile = fl }) "<file>")- "certificate file"- , Option ['k'] ["key"]- (ReqArg (\fl o -> o { optKeyFile = fl }) "<file>")- "key file"- , Option ['S'] ["retry"]- (NoArg (\o -> o { optRetry = True }))- "require stateless retry"- ]+options =+ [ Option+ ['d']+ ["debug-log-dir"]+ (ReqArg (\dir o -> o{optDebugLogDir = Just dir}) "<dir>")+ "directory to store a debug file"+ , Option+ ['q']+ ["qlog-dir"]+ (ReqArg (\dir o -> o{optQLogDir = Just dir}) "<dir>")+ "directory to store qlog"+ , Option+ ['l']+ ["key-log-file"]+ (ReqArg (\file o -> o{optKeyLogFile = Just file}) "<file>")+ "a file to store negotiated secrets"+ , Option+ ['g']+ ["groups"]+ (ReqArg (\gs o -> o{optGroups = Just gs}) "<groups>")+ "groups for key exchange"+ , Option+ ['c']+ ["cert"]+ (ReqArg (\fl o -> o{optCertFile = fl}) "<file>")+ "certificate file"+ , Option+ ['k']+ ["key"]+ (ReqArg (\fl o -> o{optKeyFile = fl}) "<file>")+ "key file"+ , Option+ ['S']+ ["retry"]+ (NoArg (\o -> o{optRetry = True}))+ "require stateless retry"+ ] usage :: String usage = "Usage: server [OPTION] addr [addrs] port"@@ -82,19 +98,19 @@ serverOpts :: [String] -> IO (Options, [String]) serverOpts argv = case getOpt Permute options argv of- (o,n,[] ) -> return (foldl (flip id) defaultOptions o, n)- (_,_,errs) -> showUsageAndExit $ concat errs+ (o, n, []) -> return (foldl (flip id) defaultOptions o, n)+ (_, _, errs) -> showUsageAndExit $ concat errs chooseALPN :: Version -> [ByteString] -> IO ByteString chooseALPN _ protos- | "perf" `elem` protos = return "perf"+ | "perf" `elem` protos = return "perf" chooseALPN ver protos = return $ case mh3idx of- Nothing -> case mhqidx of- Nothing -> ""- Just _ -> hqX- Just h3idx -> case mhqidx of- Nothing -> h3X- Just hqidx -> if h3idx < hqidx then h3X else hqX+ Nothing -> case mhqidx of+ Nothing -> ""+ Just _ -> hqX+ Just h3idx -> case mhqidx of+ Nothing -> h3X+ Just hqidx -> if h3idx < hqidx then h3X else hqX where (h3X, hqX) = makeProtos ver mh3idx = h3X `L.elemIndex` protos@@ -110,24 +126,26 @@ addrs = read <$> init ips aps = (,port) <$> addrs smgr <- SM.newSessionManager SM.defaultConfig- Right cred@(!_cc,!_priv) <- credentialLoadX509 optCertFile optKeyFile+ Right cred@(!_cc, !_priv) <- credentialLoadX509 optCertFile optKeyFile let sc0 = defaultServerConfig- sc = sc0 {- scAddresses = aps- , scALPN = Just chooseALPN- , scRequireRetry = optRetry- , scSessionManager = smgr- , scUse0RTT = True- , scDebugLog = optDebugLogDir- , scKeyLog = getLogger optKeyLogFile- , scGroups = getGroups (scGroups sc0) optGroups- , scQLog = optQLogDir- , scCredentials = Credentials [cred]- }+ sc =+ sc0+ { scAddresses = aps+ , scALPN = Just chooseALPN+ , scRequireRetry = optRetry+ , scSessionManager = smgr+ , scUse0RTT = True+ , scDebugLog = optDebugLogDir+ , scKeyLog = getLogger optKeyLogFile+ , scGroups = getGroups (scGroups sc0) optGroups+ , scQLog = optQLogDir+ , scCredentials = Credentials [cred]+ } run sc $ \conn -> do info <- getConnectionInfo conn let server = case alpn info of- Just proto | "perf" == proto -> serverPF- | "hq" `BS.isPrefixOf` proto -> serverHQ- _ -> serverH3+ Just proto+ | "perf" == proto -> serverPF+ | "hq" `BS.isPrefixOf` proto -> serverHQ+ _ -> serverH3 server conn