From 04a57d5af0d33acfd9143b20b3810cfeefdf413f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 16 Jan 2025 12:49:30 +0000 Subject: [PATCH 01/19] xftp server: use recipient ID in control port to delete and block files --- src/Simplex/FileTransfer/Server.hs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Simplex/FileTransfer/Server.hs b/src/Simplex/FileTransfer/Server.hs index 935afe2f9..407b65a70 100644 --- a/src/Simplex/FileTransfer/Server.hs +++ b/src/Simplex/FileTransfer/Server.hs @@ -287,13 +287,13 @@ xftpServer cfg@XFTPServerConfig {xftpPort, transportConfig, inactiveClientExpira CPDelete fileId -> withUserRole $ unliftIO u $ do fs <- asks store r <- runExceptT $ do - (fr, _) <- ExceptT $ atomically $ getFile fs SFSender fileId + (fr, _) <- ExceptT $ atomically $ getFile fs SFRecipient fileId ExceptT $ deleteServerFile_ fr liftIO . hPutStrLn h $ either (\e -> "error: " <> show e) (\() -> "ok") r CPBlock fileId info -> withUserRole $ unliftIO u $ do fs <- asks store r <- runExceptT $ do - (fr, _) <- ExceptT $ atomically $ getFile fs SFSender fileId + (fr, _) <- ExceptT $ atomically $ getFile fs SFRecipient fileId ExceptT $ blockServerFile fr info liftIO . hPutStrLn h $ either (\e -> "error: " <> show e) (\() -> "ok") r CPHelp -> hPutStrLn h "commands: stats-rts, delete, help, quit" @@ -540,12 +540,12 @@ blockServerFile fr@FileRec {senderId} info = do deleteOrBlockServerFile_ :: FileRec -> (FileServerStats -> IORef Int) -> (FileStore -> STM (Either XFTPErrorType ())) -> M (Either XFTPErrorType ()) deleteOrBlockServerFile_ FileRec {filePath, fileInfo} stat storeAction = runExceptT $ do - path <- readTVarIO filePath - stats <- asks serverStats - ExceptT $ first (\(_ :: SomeException) -> FILE_IO) <$> try (forM_ path $ \p -> whenM (doesFileExist p) (removeFile p >> deletedStats stats)) - st <- asks store - void $ atomically $ storeAction st - lift $ incFileStat stat + path <- readTVarIO filePath + stats <- asks serverStats + ExceptT $ first (\(_ :: SomeException) -> FILE_IO) <$> try (forM_ path $ \p -> whenM (doesFileExist p) (removeFile p >> deletedStats stats)) + st <- asks store + void $ atomically $ storeAction st + lift $ incFileStat stat where deletedStats stats = do liftIO $ atomicModifyIORef'_ (filesCount stats) (subtract 1) From fc3556a8857bda3851aa9282ef7c07f15b688fcc Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 17 Jan 2025 08:59:25 +0000 Subject: [PATCH 02/19] cap smp proxy agent version at 10 --- src/Simplex/Messaging/Server/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simplex/Messaging/Server/Main.hs b/src/Simplex/Messaging/Server/Main.hs index 0ecc5cf94..509393c94 100644 --- a/src/Simplex/Messaging/Server/Main.hs +++ b/src/Simplex/Messaging/Server/Main.hs @@ -447,7 +447,7 @@ smpServerCLI_ generateSite serveStaticFiles attachStaticFiles cfgPath logPath = defaultSMPClientAgentConfig { smpCfg = (smpCfg defaultSMPClientAgentConfig) - { serverVRange = mkVersionRange batchCmdsSMPVersion currentServerSMPRelayVersion, + { serverVRange = mkVersionRange batchCmdsSMPVersion deletedEventSMPVersion, agreeSecret = True, networkConfig = defaultNetworkConfig From d4f896964a3e58d67af5de2f02cce102f64ac07b Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 17 Jan 2025 10:12:42 +0000 Subject: [PATCH 03/19] version --- simplexmq.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplexmq.cabal b/simplexmq.cabal index cb4676bed..0f1cf7f9b 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -1,7 +1,7 @@ cabal-version: 1.12 name: simplexmq -version: 6.3.0.1 +version: 6.3.0.100 synopsis: SimpleXMQ message broker description: This package includes <./docs/Simplex-Messaging-Server.html server>, <./docs/Simplex-Messaging-Client.html client> and From 008aecbe42bd4c992de86575477759293e8010ac Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 17 Jan 2025 10:15:10 +0000 Subject: [PATCH 04/19] fix prometheus --- src/Simplex/Messaging/Server/Prometheus.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Simplex/Messaging/Server/Prometheus.hs b/src/Simplex/Messaging/Server/Prometheus.hs index cb9c68d04..3f5c3f87e 100644 --- a/src/Simplex/Messaging/Server/Prometheus.hs +++ b/src/Simplex/Messaging/Server/Prometheus.hs @@ -124,8 +124,8 @@ prometheusMetrics sm rtm ts = \simplex_smp_queues_deleted{type=\"new\"} " <> mshow _qDeletedNew <> "\n# qDeletedNew\n\ \simplex_smp_queues_deleted{type=\"secured\"} " <> mshow _qDeletedSecured <> "\n# qDeletedSecured\n\ \\n\ - \# HELP simplex_smp_queues_deleted Deleted queues\n\ - \# TYPE simplex_smp_queues_deleted counter\n\ + \# HELP simplex_smp_queues_blocked Deleted queues\n\ + \# TYPE simplex_smp_queues_blocked counter\n\ \simplex_smp_queues_blocked " <> mshow _qBlocked <> "\n# qBlocked\n\ \\n\ \# HELP simplex_smp_queues_deleted_batch Batched requests to delete queues\n\ From 4d76a5560e2bf8913d5f3d6bce19f23fdea50274 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 17 Jan 2025 10:18:31 +0000 Subject: [PATCH 05/19] fix --- src/Simplex/Messaging/Server/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simplex/Messaging/Server/Main.hs b/src/Simplex/Messaging/Server/Main.hs index 509393c94..d2704ff21 100644 --- a/src/Simplex/Messaging/Server/Main.hs +++ b/src/Simplex/Messaging/Server/Main.hs @@ -47,7 +47,7 @@ import Simplex.Messaging.Server.Information import Simplex.Messaging.Server.MsgStore.Journal (JournalStoreConfig (..)) import Simplex.Messaging.Server.MsgStore.Types (AMSType (..), SMSType (..), newMsgStore) import Simplex.Messaging.Server.QueueStore.STM (readQueueStore) -import Simplex.Messaging.Transport (batchCmdsSMPVersion, currentServerSMPRelayVersion, simplexMQVersion, supportedServerSMPRelayVRange) +import Simplex.Messaging.Transport (batchCmdsSMPVersion, currentServerSMPRelayVersion, deletedEventSMPVersion, simplexMQVersion, supportedServerSMPRelayVRange) import Simplex.Messaging.Transport.Client (SocksProxy, TransportHost (..), defaultSocksProxy) import Simplex.Messaging.Transport.Server (ServerCredentials (..), TransportServerConfig (..), defaultTransportServerConfig) import Simplex.Messaging.Util (eitherToMaybe, ifM, safeDecodeUtf8, tshow) From 86b6b340f518a684ec394e1e246f446744e6b90c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 17 Jan 2025 17:39:45 +0000 Subject: [PATCH 06/19] remove old version support --- src/Simplex/Messaging/Protocol.hs | 8 ++----- src/Simplex/Messaging/Server/Main.hs | 5 ++-- src/Simplex/Messaging/Transport.hs | 32 +++++++++++++++----------- tests/AgentTests/FunctionalAPITests.hs | 20 ++++------------ tests/CoreTests/BatchingTests.hs | 6 ++--- tests/SMPClient.hs | 6 ++--- tests/SMPProxyTests.hs | 6 ++--- tests/ServerTests.hs | 2 +- 8 files changed, 37 insertions(+), 48 deletions(-) diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index 2a76faa05..c60aa5dc2 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -1370,9 +1370,7 @@ instance PartyI p => ProtocolEncoding SMPVersion ErrorType (Command p) where encodeProtocol v = \case NEW rKey dhKey auth_ subMode sndSecure | v >= sndAuthKeySMPVersion -> new <> e (auth_, subMode, sndSecure) - | v >= subModeSMPVersion -> new <> auth <> e subMode - | v == basicAuthSMPVersion -> new <> auth - | otherwise -> new + | otherwise -> new <> auth <> e subMode where new = e (NEW_, ' ', rKey, dhKey) auth = maybe "" (e . ('A',)) auth_ @@ -1441,9 +1439,7 @@ instance ProtocolEncoding SMPVersion ErrorType Cmd where Cmd SRecipient <$> case tag of NEW_ | v >= sndAuthKeySMPVersion -> new <*> smpP <*> smpP <*> smpP - | v >= subModeSMPVersion -> new <*> auth <*> smpP <*> pure False - | v == basicAuthSMPVersion -> new <*> auth <*> pure SMSubscribe <*> pure False - | otherwise -> new <*> pure Nothing <*> pure SMSubscribe <*> pure False + | otherwise -> new <*> auth <*> smpP <*> pure False where new = NEW <$> _smpP <*> smpP auth = optional (A.char 'A' *> smpP) diff --git a/src/Simplex/Messaging/Server/Main.hs b/src/Simplex/Messaging/Server/Main.hs index d2704ff21..1ceb64d6e 100644 --- a/src/Simplex/Messaging/Server/Main.hs +++ b/src/Simplex/Messaging/Server/Main.hs @@ -47,11 +47,10 @@ import Simplex.Messaging.Server.Information import Simplex.Messaging.Server.MsgStore.Journal (JournalStoreConfig (..)) import Simplex.Messaging.Server.MsgStore.Types (AMSType (..), SMSType (..), newMsgStore) import Simplex.Messaging.Server.QueueStore.STM (readQueueStore) -import Simplex.Messaging.Transport (batchCmdsSMPVersion, currentServerSMPRelayVersion, deletedEventSMPVersion, simplexMQVersion, supportedServerSMPRelayVRange) +import Simplex.Messaging.Transport (simplexMQVersion, supportedProxyClientSMPRelayVRange, supportedServerSMPRelayVRange) import Simplex.Messaging.Transport.Client (SocksProxy, TransportHost (..), defaultSocksProxy) import Simplex.Messaging.Transport.Server (ServerCredentials (..), TransportServerConfig (..), defaultTransportServerConfig) import Simplex.Messaging.Util (eitherToMaybe, ifM, safeDecodeUtf8, tshow) -import Simplex.Messaging.Version (mkVersionRange) import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist) import System.Exit (exitFailure) import System.FilePath (combine) @@ -447,7 +446,7 @@ smpServerCLI_ generateSite serveStaticFiles attachStaticFiles cfgPath logPath = defaultSMPClientAgentConfig { smpCfg = (smpCfg defaultSMPClientAgentConfig) - { serverVRange = mkVersionRange batchCmdsSMPVersion deletedEventSMPVersion, + { serverVRange = supportedProxyClientSMPRelayVRange, agreeSecret = True, networkConfig = defaultNetworkConfig diff --git a/src/Simplex/Messaging/Transport.hs b/src/Simplex/Messaging/Transport.hs index d4601d569..f965f41ad 100644 --- a/src/Simplex/Messaging/Transport.hs +++ b/src/Simplex/Messaging/Transport.hs @@ -36,14 +36,14 @@ module Simplex.Messaging.Transport supportedSMPHandshakes, supportedClientSMPRelayVRange, supportedServerSMPRelayVRange, + supportedProxyClientSMPRelayVRange, proxiedSMPRelayVRange, + minClientSMPRelayVersion, + minServerSMPRelayVersion, legacyServerSMPRelayVRange, currentClientSMPRelayVersion, legacyServerSMPRelayVersion, currentServerSMPRelayVersion, - batchCmdsSMPVersion, - basicAuthSMPVersion, - subModeSMPVersion, authCmdsSMPVersion, sendingProxySMPVersion, sndAuthKeySMPVersion, @@ -156,14 +156,8 @@ type VersionRangeSMP = VersionRange SMPVersion pattern VersionSMP :: Word16 -> VersionSMP pattern VersionSMP v = Version v -batchCmdsSMPVersion :: VersionSMP -batchCmdsSMPVersion = VersionSMP 4 - -basicAuthSMPVersion :: VersionSMP -basicAuthSMPVersion = VersionSMP 5 - -subModeSMPVersion :: VersionSMP -subModeSMPVersion = VersionSMP 6 +_subModeSMPVersion :: VersionSMP +_subModeSMPVersion = VersionSMP 6 authCmdsSMPVersion :: VersionSMP authCmdsSMPVersion = VersionSMP 7 @@ -183,6 +177,12 @@ encryptedBlockSMPVersion = VersionSMP 11 blockedEntityErrorSMPVersion :: VersionSMP blockedEntityErrorSMPVersion = VersionSMP 12 +minClientSMPRelayVersion :: VersionSMP +minClientSMPRelayVersion = VersionSMP 6 + +minServerSMPRelayVersion :: VersionSMP +minServerSMPRelayVersion = VersionSMP 6 + currentClientSMPRelayVersion :: VersionSMP currentClientSMPRelayVersion = VersionSMP 12 @@ -203,13 +203,17 @@ proxiedSMPRelayVersion = VersionSMP 12 -- minimal supported protocol version is 4 -- TODO remove code that supports sending commands without batching supportedClientSMPRelayVRange :: VersionRangeSMP -supportedClientSMPRelayVRange = mkVersionRange batchCmdsSMPVersion currentClientSMPRelayVersion +supportedClientSMPRelayVRange = mkVersionRange minClientSMPRelayVersion currentClientSMPRelayVersion legacyServerSMPRelayVRange :: VersionRangeSMP -legacyServerSMPRelayVRange = mkVersionRange batchCmdsSMPVersion legacyServerSMPRelayVersion +legacyServerSMPRelayVRange = mkVersionRange minServerSMPRelayVersion legacyServerSMPRelayVersion supportedServerSMPRelayVRange :: VersionRangeSMP -supportedServerSMPRelayVRange = mkVersionRange batchCmdsSMPVersion currentServerSMPRelayVersion +supportedServerSMPRelayVRange = mkVersionRange minServerSMPRelayVersion currentServerSMPRelayVersion + +-- cap it temporarily to version 10 +supportedProxyClientSMPRelayVRange :: VersionRangeSMP +supportedProxyClientSMPRelayVRange = mkVersionRange minServerSMPRelayVersion deletedEventSMPVersion -- This range initially allows only version 8 - see the comment above. proxiedSMPRelayVRange :: VersionRangeSMP diff --git a/tests/AgentTests/FunctionalAPITests.hs b/tests/AgentTests/FunctionalAPITests.hs index f569cc3d5..fa87276a0 100644 --- a/tests/AgentTests/FunctionalAPITests.hs +++ b/tests/AgentTests/FunctionalAPITests.hs @@ -99,7 +99,7 @@ import Simplex.Messaging.Server.Env.STM (ServerConfig (..)) import Simplex.Messaging.Server.Expiration import Simplex.Messaging.Server.MsgStore.Types (AMSType (..), SMSType (..)) import Simplex.Messaging.Server.QueueStore.QueueInfo -import Simplex.Messaging.Transport (ATransport (..), SMPVersion, VersionSMP, authCmdsSMPVersion, basicAuthSMPVersion, batchCmdsSMPVersion, currentServerSMPRelayVersion, sndAuthKeySMPVersion, supportedSMPHandshakes) +import Simplex.Messaging.Transport (ATransport (..), SMPVersion, VersionSMP, authCmdsSMPVersion, currentServerSMPRelayVersion, minClientSMPRelayVersion, minServerSMPRelayVersion, sndAuthKeySMPVersion, supportedSMPHandshakes) import Simplex.Messaging.Util (bshow, diffToMicroseconds) import Simplex.Messaging.Version (VersionRange (..)) import qualified Simplex.Messaging.Version as V @@ -425,7 +425,6 @@ functionalAPITests t = do describe "should switch two connections simultaneously, abort one" $ testServerMatrix2 t testSwitch2ConnectionsAbort1 describe "SMP basic auth" $ do - let v4 = prevVersion basicAuthSMPVersion forM_ (nub [prevVersion authCmdsSMPVersion, authCmdsSMPVersion, currentServerSMPRelayVersion]) $ \v -> do let baseId = if v >= sndAuthKeySMPVersion then 1 else 3 sqSecured = if v >= sndAuthKeySMPVersion then True else False @@ -435,20 +434,12 @@ functionalAPITests t = do it "disabled " $ testBasicAuth t False (Just "abcd", v) (Just "abcd", v) (Just "abcd", v) sqSecured baseId `shouldReturn` 0 it "NEW fail, no auth " $ testBasicAuth t True (Just "abcd", v) (Nothing, v) (Just "abcd", v) sqSecured baseId `shouldReturn` 0 it "NEW fail, bad auth " $ testBasicAuth t True (Just "abcd", v) (Just "wrong", v) (Just "abcd", v) sqSecured baseId `shouldReturn` 0 - it "NEW fail, version " $ testBasicAuth t True (Just "abcd", v) (Just "abcd", v4) (Just "abcd", v) sqSecured baseId `shouldReturn` 0 it "JOIN fail, no auth " $ testBasicAuth t True (Just "abcd", v) (Just "abcd", v) (Nothing, v) sqSecured baseId `shouldReturn` 1 it "JOIN fail, bad auth " $ testBasicAuth t True (Just "abcd", v) (Just "abcd", v) (Just "wrong", v) sqSecured baseId `shouldReturn` 1 - it "JOIN fail, version " $ testBasicAuth t True (Just "abcd", v) (Just "abcd", v) (Just "abcd", v4) sqSecured baseId `shouldReturn` 1 describe ("v" <> show v <> ": no server auth") $ do it "success " $ testBasicAuth t True (Nothing, v) (Nothing, v) (Nothing, v) sqSecured baseId `shouldReturn` 2 it "srv disabled" $ testBasicAuth t False (Nothing, v) (Nothing, v) (Nothing, v) sqSecured baseId `shouldReturn` 0 - it "version srv " $ testBasicAuth t True (Nothing, v4) (Nothing, v) (Nothing, v) False 3 `shouldReturn` 2 - it "version fst " $ testBasicAuth t True (Nothing, v) (Nothing, v4) (Nothing, v) False baseId `shouldReturn` 2 - it "version snd " $ testBasicAuth t True (Nothing, v) (Nothing, v) (Nothing, v4) sqSecured 3 `shouldReturn` 2 - it "version both" $ testBasicAuth t True (Nothing, v) (Nothing, v4) (Nothing, v4) False 3 `shouldReturn` 2 - it "version all " $ testBasicAuth t True (Nothing, v4) (Nothing, v4) (Nothing, v4) False 3 `shouldReturn` 2 it "auth fst " $ testBasicAuth t True (Nothing, v) (Just "abcd", v) (Nothing, v) sqSecured baseId `shouldReturn` 2 - it "auth fst 2 " $ testBasicAuth t True (Nothing, v4) (Just "abcd", v) (Nothing, v) False 3 `shouldReturn` 2 it "auth snd " $ testBasicAuth t True (Nothing, v) (Nothing, v) (Just "abcd", v) sqSecured baseId `shouldReturn` 2 it "auth both " $ testBasicAuth t True (Nothing, v) (Just "abcd", v) (Just "abcd", v) sqSecured baseId `shouldReturn` 2 it "auth, disabled" $ testBasicAuth t False (Nothing, v) (Just "abcd", v) (Just "abcd", v) sqSecured baseId `shouldReturn` 0 @@ -481,7 +472,7 @@ functionalAPITests t = do testBasicAuth :: ATransport -> Bool -> (Maybe BasicAuth, VersionSMP) -> (Maybe BasicAuth, VersionSMP) -> (Maybe BasicAuth, VersionSMP) -> SndQueueSecured -> AgentMsgId -> IO Int testBasicAuth t allowNewQueues srv@(srvAuth, srvVersion) clnt1 clnt2 sqSecured baseId = do - let testCfg = cfg {allowNewQueues, newQueueBasicAuth = srvAuth, smpServerVRange = V.mkVersionRange batchCmdsSMPVersion srvVersion} + let testCfg = cfg {allowNewQueues, newQueueBasicAuth = srvAuth, smpServerVRange = V.mkVersionRange minServerSMPRelayVersion srvVersion} canCreate1 = canCreateQueue allowNewQueues srv clnt1 canCreate2 = canCreateQueue allowNewQueues srv clnt2 expected @@ -493,9 +484,8 @@ testBasicAuth t allowNewQueues srv@(srvAuth, srvVersion) clnt1 clnt2 sqSecured b pure created canCreateQueue :: Bool -> (Maybe BasicAuth, VersionSMP) -> (Maybe BasicAuth, VersionSMP) -> Bool -canCreateQueue allowNew (srvAuth, srvVersion) (clntAuth, clntVersion) = - let v = basicAuthSMPVersion - in allowNew && (isNothing srvAuth || (srvVersion >= v && clntVersion >= v && srvAuth == clntAuth)) +canCreateQueue allowNew (srvAuth, _) (clntAuth, _) = + allowNew && (isNothing srvAuth || srvAuth == clntAuth) testMatrix2 :: HasCallStack => ATransport -> (PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec testMatrix2 t runTest = do @@ -2889,7 +2879,7 @@ testCreateQueueAuth srvVersion clnt1 clnt2 sqSecured baseId = do getClient clientId (clntAuth, clntVersion) db = let servers = initAgentServers {smp = userServers' [ProtoServerWithAuth testSMPServer clntAuth]} alpn_ = if clntVersion >= authCmdsSMPVersion then Just supportedSMPHandshakes else Nothing - smpCfg = defaultClientConfig alpn_ False $ V.mkVersionRange (prevVersion basicAuthSMPVersion) clntVersion + smpCfg = defaultClientConfig alpn_ False $ V.mkVersionRange minClientSMPRelayVersion clntVersion sndAuthAlg = if srvVersion >= authCmdsSMPVersion && clntVersion >= authCmdsSMPVersion then C.AuthAlg C.SX25519 else C.AuthAlg C.SEd25519 in getSMPAgentClient' clientId agentCfg {smpCfg, sndAuthAlg} servers db diff --git a/tests/CoreTests/BatchingTests.hs b/tests/CoreTests/BatchingTests.hs index 41b1a6a38..3e6a3fa40 100644 --- a/tests/CoreTests/BatchingTests.hs +++ b/tests/CoreTests/BatchingTests.hs @@ -296,7 +296,7 @@ testClientStubV6 :: IO (ProtocolClient SMPVersion ErrorType BrokerMsg) testClientStubV6 = do g <- C.newRandom sessId <- atomically $ C.randomBytes 32 g - smpClientStub g sessId subModeSMPVersion Nothing + smpClientStub g sessId minServerSMPRelayVersion Nothing testClientStub :: IO (ProtocolClient SMPVersion ErrorType BrokerMsg) testClientStub = do @@ -307,7 +307,7 @@ testClientStub = do smpClientStub g sessId currentClientSMPRelayVersion thAuth_ randomSUBv6 :: ByteString -> IO (Either TransportError (Maybe TransmissionAuth, ByteString)) -randomSUBv6 = randomSUB_ C.SEd25519 subModeSMPVersion +randomSUBv6 = randomSUB_ C.SEd25519 minServerSMPRelayVersion randomSUB :: ByteString -> IO (Either TransportError (Maybe TransmissionAuth, ByteString)) randomSUB = randomSUB_ C.SEd25519 currentClientSMPRelayVersion @@ -354,7 +354,7 @@ randomNMSGCmd ts = do pure (CorrId "", EntityId nId, NMSG nonce encNMsgMeta) randomSENDv6 :: ByteString -> Int -> IO (Either TransportError (Maybe TransmissionAuth, ByteString)) -randomSENDv6 = randomSEND_ C.SEd25519 subModeSMPVersion +randomSENDv6 = randomSEND_ C.SEd25519 minServerSMPRelayVersion randomSEND :: ByteString -> Int -> IO (Either TransportError (Maybe TransmissionAuth, ByteString)) randomSEND = randomSEND_ C.SX25519 currentClientSMPRelayVersion diff --git a/tests/SMPClient.hs b/tests/SMPClient.hs index 5f7935cd9..8a1091ef1 100644 --- a/tests/SMPClient.hs +++ b/tests/SMPClient.hs @@ -167,10 +167,10 @@ cfgMS msType = } cfgV7 :: ServerConfig -cfgV7 = cfg {smpServerVRange = mkVersionRange batchCmdsSMPVersion authCmdsSMPVersion} +cfgV7 = cfg {smpServerVRange = mkVersionRange minServerSMPRelayVersion authCmdsSMPVersion} cfgV8 :: ServerConfig -cfgV8 = cfg {smpServerVRange = mkVersionRange batchCmdsSMPVersion sendingProxySMPVersion} +cfgV8 = cfg {smpServerVRange = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion} cfgVPrev :: ServerConfig cfgVPrev = cfg {smpServerVRange = prevRange $ smpServerVRange cfg} @@ -191,7 +191,7 @@ proxyCfg = smpAgentCfg' = smpAgentCfg cfg proxyVRangeV8 :: VersionRangeSMP -proxyVRangeV8 = mkVersionRange batchCmdsSMPVersion sendingProxySMPVersion +proxyVRangeV8 = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion withSmpServerStoreMsgLogOn :: HasCallStack => ATransport -> ServiceName -> (HasCallStack => ThreadId -> IO a) -> IO a withSmpServerStoreMsgLogOn = (`withSmpServerStoreMsgLogOnMS` AMSType SMSJournal) diff --git a/tests/SMPProxyTests.hs b/tests/SMPProxyTests.hs index cbdc7a3f5..2b218044d 100644 --- a/tests/SMPProxyTests.hs +++ b/tests/SMPProxyTests.hs @@ -162,12 +162,12 @@ deliverMessagesViaProxy proxyServ relayServ alg unsecuredMsgs securedMsgs = do g <- C.newRandom -- set up proxy ts <- getCurrentTime - pc' <- getProtocolClient g (1, proxyServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange batchCmdsSMPVersion sendingProxySMPVersion} Nothing ts (\_ -> pure ()) + pc' <- getProtocolClient g (1, proxyServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion} Nothing ts (\_ -> pure ()) pc <- either (fail . show) pure pc' THAuthClient {} <- maybe (fail "getProtocolClient returned no thAuth") pure $ thAuth $ thParams pc -- set up relay msgQ <- newTBQueueIO 1024 - rc' <- getProtocolClient g (2, relayServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange batchCmdsSMPVersion authCmdsSMPVersion} (Just msgQ) ts (\_ -> pure ()) + rc' <- getProtocolClient g (2, relayServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange minServerSMPRelayVersion authCmdsSMPVersion} (Just msgQ) ts (\_ -> pure ()) rc <- either (fail . show) pure rc' -- prepare receiving queue (rPub, rPriv) <- atomically $ C.generateAuthKeyPair alg g @@ -205,7 +205,7 @@ proxyConnectDeadRelay n d proxyServ = do g <- C.newRandom -- set up proxy ts <- getCurrentTime - pc' <- getProtocolClient g (1, proxyServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange batchCmdsSMPVersion sendingProxySMPVersion} Nothing ts (\_ -> pure ()) + pc' <- getProtocolClient g (1, proxyServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion} Nothing ts (\_ -> pure ()) pc <- either (fail . show) pure pc' THAuthClient {} <- maybe (fail "getProtocolClient returned no thAuth") pure $ thAuth $ thParams pc -- get proxy session diff --git a/tests/ServerTests.hs b/tests/ServerTests.hs index 9cde80286..088a7b977 100644 --- a/tests/ServerTests.hs +++ b/tests/ServerTests.hs @@ -851,7 +851,7 @@ testTiming = describe "should have similar time for auth error, whether queue exists or not, for all key types" $ forM_ timingTests $ \tst -> it (testName tst) $ \(ATransport t, msType) -> - smpTest2Cfg (cfgMS msType) (mkVersionRange batchCmdsSMPVersion authCmdsSMPVersion) t $ \rh sh -> + smpTest2Cfg (cfgMS msType) (mkVersionRange minServerSMPRelayVersion authCmdsSMPVersion) t $ \rh sh -> testSameTiming rh sh tst where testName :: (C.AuthAlg, C.AuthAlg, Int) -> String From 354035babdb88d40e36d0977e97fb16c6b108f38 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 17 Jan 2025 21:28:27 +0000 Subject: [PATCH 07/19] log connection parameter on error --- simplexmq.cabal | 2 +- src/Simplex/Messaging/Protocol.hs | 20 ++++++++++++++++--- src/Simplex/Messaging/Transport.hs | 2 +- tests/SMPClient.hs | 10 +++++++++- tests/SMPProxyTests.hs | 32 ++++++++++++++++++++++++++++-- tests/Test.hs | 2 +- 6 files changed, 59 insertions(+), 9 deletions(-) diff --git a/simplexmq.cabal b/simplexmq.cabal index 0f1cf7f9b..4f655da8a 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -1,7 +1,7 @@ cabal-version: 1.12 name: simplexmq -version: 6.3.0.100 +version: 6.3.0.101 synopsis: SimpleXMQ message broker description: This package includes <./docs/Simplex-Messaging-Server.html server>, <./docs/Simplex-Messaging-Client.html client> and diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index c60aa5dc2..fd36d2911 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -260,7 +260,7 @@ supportedSMPClientVRange = mkVersionRange initialSMPClientVersion currentSMPClie -- TODO v6.0 remove dependency on version maxMessageLength :: VersionSMP -> Int maxMessageLength v - | v >= encryptedBlockSMPVersion = 16048 -- max 16051 + | v >= encryptedBlockSMPVersion = 16048 -- max 16048 | v >= sendingProxySMPVersion = 16064 -- max 16067 | otherwise = 16088 -- 16048 - always use this size to determine allowed ranges @@ -1694,12 +1694,26 @@ tPut th@THandle {params} = fmap concat . mapM tPutBatch . batchTransmissions (ba TBTransmission s _ -> (: []) <$> tPutLog th s tPutLog :: Transport c => THandle v c p -> ByteString -> IO (Either TransportError ()) -tPutLog th s = do +tPutLog th@THandle {params} s = do r <- tPutBlock th s case r of - Left e -> putStrLn ("tPut error: " <> show e) + Left e -> putStrLn ("tPut error: " <> show e <> paramsStr) _ -> pure () pure r + where + paramsStr = + ", block size" + <> show (B.length s) + <> ", thServerVRange = " + <> show (thServerVRange params) + <> ", thVersion = " + <> show (thVersion params) + <> ", thAuth = " + <> show (isJust $ thAuth params) + <> ", implySessId = " + <> show (implySessId params) + <> ", encryptBlock = " + <> show (isJust $ encryptBlock params) -- ByteString in TBTransmissions includes byte with transmissions count data TransportBatch r = TBTransmissions ByteString Int [r] | TBTransmission ByteString r | TBError TransportError r diff --git a/src/Simplex/Messaging/Transport.hs b/src/Simplex/Messaging/Transport.hs index f965f41ad..62f42a17d 100644 --- a/src/Simplex/Messaging/Transport.hs +++ b/src/Simplex/Messaging/Transport.hs @@ -213,7 +213,7 @@ supportedServerSMPRelayVRange = mkVersionRange minServerSMPRelayVersion currentS -- cap it temporarily to version 10 supportedProxyClientSMPRelayVRange :: VersionRangeSMP -supportedProxyClientSMPRelayVRange = mkVersionRange minServerSMPRelayVersion deletedEventSMPVersion +supportedProxyClientSMPRelayVRange = mkVersionRange minServerSMPRelayVersion currentServerSMPRelayVersion -- This range initially allows only version 8 - see the comment above. proxiedSMPRelayVRange :: VersionRangeSMP diff --git a/tests/SMPClient.hs b/tests/SMPClient.hs index 8a1091ef1..a92b9e132 100644 --- a/tests/SMPClient.hs +++ b/tests/SMPClient.hs @@ -185,11 +185,19 @@ proxyCfg :: ServerConfig proxyCfg = cfg { allowSMPProxy = True, - smpAgentCfg = smpAgentCfg' {smpCfg = (smpCfg smpAgentCfg') {agreeSecret = True}} + smpAgentCfg = smpAgentCfg' {smpCfg = (smpCfg smpAgentCfg') {agreeSecret = True, serverVRange = supportedProxyClientSMPRelayVRange}} } where smpAgentCfg' = smpAgentCfg cfg +proxyCfgV62 :: ServerConfig +proxyCfgV62 = + proxyCfg + { smpAgentCfg = smpAgentCfg' {smpCfg = (smpCfg smpAgentCfg') {serverVRange = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion}} + } + where + smpAgentCfg' = smpAgentCfg proxyCfg + proxyVRangeV8 :: VersionRangeSMP proxyVRangeV8 = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion diff --git a/tests/SMPProxyTests.hs b/tests/SMPProxyTests.hs index 2b218044d..6e4302bb9 100644 --- a/tests/SMPProxyTests.hs +++ b/tests/SMPProxyTests.hs @@ -68,11 +68,11 @@ smpProxyTests = do let srv1 = SMPServer testHost testPort testKeyHash srv2 = SMPServer testHost2 testPort2 testKeyHash describe "client API" $ do - let maxLen = maxMessageLength encryptedBlockSMPVersion + let maxLen = 16048 -- maxMessageLength encryptedBlockSMPVersion describe "one server" $ do it "deliver via proxy" . oneServer $ do deliverMessageViaProxy srv1 srv1 C.SEd448 "hello 1" "hello 2" - describe "two servers" $ do + fdescribe "two servers" $ do let proxyServ = srv1 relayServ = srv2 (msg1, msg2) <- runIO $ do @@ -86,6 +86,34 @@ smpProxyTests = do deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 it "max message size, X25519 keys" . twoServersFirstProxy $ deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 + fdescribe "new server and old proxy" $ do + let proxyServ = srv1 + relayServ = srv2 + (msg1, msg2) <- runIO $ do + g <- C.newRandom + atomically $ (,) <$> C.randomBytes maxLen g <*> C.randomBytes maxLen g + it "deliver via proxy" . twoServers_ proxyCfgV62 proxyCfg $ + deliverMessageViaProxy proxyServ relayServ C.SEd448 "hello 1" "hello 2" + it "max message size, Ed448 keys" . twoServers_ proxyCfgV62 proxyCfg $ + deliverMessageViaProxy proxyServ relayServ C.SEd448 msg1 msg2 + it "max message size, Ed25519 keys" . twoServers_ proxyCfgV62 proxyCfg $ + deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 + it "max message size, X25519 keys" . twoServers_ proxyCfgV62 proxyCfg $ + deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 + fdescribe "old server and new proxy" $ do + let proxyServ = srv1 + relayServ = srv2 + (msg1, msg2) <- runIO $ do + g <- C.newRandom + atomically $ (,) <$> C.randomBytes maxLen g <*> C.randomBytes maxLen g + it "deliver via proxy" . twoServers_ proxyCfg proxyCfgV62 $ + deliverMessageViaProxy proxyServ relayServ C.SEd448 "hello 1" "hello 2" + it "max message size, Ed448 keys" . twoServers_ proxyCfg proxyCfgV62 $ + deliverMessageViaProxy proxyServ relayServ C.SEd448 msg1 msg2 + it "max message size, Ed25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ + deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 + it "max message size, X25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ + deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 describe "stress test 1k" $ do let deliver n = deliverMessagesViaProxy srv1 srv2 C.SEd448 [] (map bshow [1 :: Int .. n]) it "1x1000" . twoServersFirstProxy $ deliver 1000 diff --git a/tests/Test.hs b/tests/Test.hs index 09fb856fd..99e30aba5 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -84,7 +84,7 @@ main = do -- before (pure (transport @WS, AMSType SMSJournal)) serverTests describe "Notifications server" $ ntfServerTests (transport @TLS) describe "SMP client agent" $ agentTests (transport @TLS) - describe "SMP proxy" smpProxyTests + fdescribe "SMP proxy" smpProxyTests describe "XFTP" $ do describe "XFTP server" xftpServerTests describe "XFTP file description" fileDescriptionTests From 6ab06d4635887c668d9fb25507a5f231208241c2 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 17 Jan 2025 21:29:30 +0000 Subject: [PATCH 08/19] tests --- tests/SMPProxyTests.hs | 60 +++++++++++++++++++++--------------------- tests/Test.hs | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/SMPProxyTests.hs b/tests/SMPProxyTests.hs index 6e4302bb9..929ab9e42 100644 --- a/tests/SMPProxyTests.hs +++ b/tests/SMPProxyTests.hs @@ -68,11 +68,11 @@ smpProxyTests = do let srv1 = SMPServer testHost testPort testKeyHash srv2 = SMPServer testHost2 testPort2 testKeyHash describe "client API" $ do - let maxLen = 16048 -- maxMessageLength encryptedBlockSMPVersion + let maxLen = maxMessageLength encryptedBlockSMPVersion describe "one server" $ do it "deliver via proxy" . oneServer $ do deliverMessageViaProxy srv1 srv1 C.SEd448 "hello 1" "hello 2" - fdescribe "two servers" $ do + describe "two servers" $ do let proxyServ = srv1 relayServ = srv2 (msg1, msg2) <- runIO $ do @@ -86,34 +86,34 @@ smpProxyTests = do deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 it "max message size, X25519 keys" . twoServersFirstProxy $ deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 - fdescribe "new server and old proxy" $ do - let proxyServ = srv1 - relayServ = srv2 - (msg1, msg2) <- runIO $ do - g <- C.newRandom - atomically $ (,) <$> C.randomBytes maxLen g <*> C.randomBytes maxLen g - it "deliver via proxy" . twoServers_ proxyCfgV62 proxyCfg $ - deliverMessageViaProxy proxyServ relayServ C.SEd448 "hello 1" "hello 2" - it "max message size, Ed448 keys" . twoServers_ proxyCfgV62 proxyCfg $ - deliverMessageViaProxy proxyServ relayServ C.SEd448 msg1 msg2 - it "max message size, Ed25519 keys" . twoServers_ proxyCfgV62 proxyCfg $ - deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 - it "max message size, X25519 keys" . twoServers_ proxyCfgV62 proxyCfg $ - deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 - fdescribe "old server and new proxy" $ do - let proxyServ = srv1 - relayServ = srv2 - (msg1, msg2) <- runIO $ do - g <- C.newRandom - atomically $ (,) <$> C.randomBytes maxLen g <*> C.randomBytes maxLen g - it "deliver via proxy" . twoServers_ proxyCfg proxyCfgV62 $ - deliverMessageViaProxy proxyServ relayServ C.SEd448 "hello 1" "hello 2" - it "max message size, Ed448 keys" . twoServers_ proxyCfg proxyCfgV62 $ - deliverMessageViaProxy proxyServ relayServ C.SEd448 msg1 msg2 - it "max message size, Ed25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ - deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 - it "max message size, X25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ - deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 + -- describe "new server and old proxy" $ do + -- let proxyServ = srv1 + -- relayServ = srv2 + -- (msg1, msg2) <- runIO $ do + -- g <- C.newRandom + -- atomically $ (,) <$> C.randomBytes maxLen g <*> C.randomBytes maxLen g + -- it "deliver via proxy" . twoServers_ proxyCfgV62 proxyCfg $ + -- deliverMessageViaProxy proxyServ relayServ C.SEd448 "hello 1" "hello 2" + -- it "max message size, Ed448 keys" . twoServers_ proxyCfgV62 proxyCfg $ + -- deliverMessageViaProxy proxyServ relayServ C.SEd448 msg1 msg2 + -- it "max message size, Ed25519 keys" . twoServers_ proxyCfgV62 proxyCfg $ + -- deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 + -- it "max message size, X25519 keys" . twoServers_ proxyCfgV62 proxyCfg $ + -- deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 + -- describe "old server and new proxy" $ do + -- let proxyServ = srv1 + -- relayServ = srv2 + -- (msg1, msg2) <- runIO $ do + -- g <- C.newRandom + -- atomically $ (,) <$> C.randomBytes maxLen g <*> C.randomBytes maxLen g + -- it "deliver via proxy" . twoServers_ proxyCfg proxyCfgV62 $ + -- deliverMessageViaProxy proxyServ relayServ C.SEd448 "hello 1" "hello 2" + -- it "max message size, Ed448 keys" . twoServers_ proxyCfg proxyCfgV62 $ + -- deliverMessageViaProxy proxyServ relayServ C.SEd448 msg1 msg2 + -- it "max message size, Ed25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ + -- deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 + -- it "max message size, X25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ + -- deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 describe "stress test 1k" $ do let deliver n = deliverMessagesViaProxy srv1 srv2 C.SEd448 [] (map bshow [1 :: Int .. n]) it "1x1000" . twoServersFirstProxy $ deliver 1000 diff --git a/tests/Test.hs b/tests/Test.hs index 99e30aba5..09fb856fd 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -84,7 +84,7 @@ main = do -- before (pure (transport @WS, AMSType SMSJournal)) serverTests describe "Notifications server" $ ntfServerTests (transport @TLS) describe "SMP client agent" $ agentTests (transport @TLS) - fdescribe "SMP proxy" smpProxyTests + describe "SMP proxy" smpProxyTests describe "XFTP" $ do describe "XFTP server" xftpServerTests describe "XFTP file description" fileDescriptionTests From 6316e438f8789772db1d38755ad242ce3ead12f8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Jan 2025 11:44:33 +0000 Subject: [PATCH 09/19] log sent command tag --- simplexmq.cabal | 2 +- src/Simplex/FileTransfer/Protocol.hs | 2 + src/Simplex/Messaging/Client.hs | 67 ++++++++++--------- src/Simplex/Messaging/Notifications/Client.hs | 2 +- .../Messaging/Notifications/Protocol.hs | 2 + src/Simplex/Messaging/Protocol.hs | 3 +- 6 files changed, 43 insertions(+), 35 deletions(-) diff --git a/simplexmq.cabal b/simplexmq.cabal index 4f655da8a..9af356628 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -1,7 +1,7 @@ cabal-version: 1.12 name: simplexmq -version: 6.3.0.101 +version: 6.3.0.102 synopsis: SimpleXMQ message broker description: This package includes <./docs/Simplex-Messaging-Server.html server>, <./docs/Simplex-Messaging-Client.html client> and diff --git a/src/Simplex/FileTransfer/Protocol.hs b/src/Simplex/FileTransfer/Protocol.hs index 9bf552732..ac736ed94 100644 --- a/src/Simplex/FileTransfer/Protocol.hs +++ b/src/Simplex/FileTransfer/Protocol.hs @@ -111,6 +111,8 @@ deriving instance Show (FileCommandTag p) data FileCmdTag = forall p. FilePartyI p => FCT (SFileParty p) (FileCommandTag p) +deriving instance Show FileCmdTag + instance FilePartyI p => Encoding (FileCommandTag p) where smpEncode = \case FNEW_ -> "FNEW" diff --git a/src/Simplex/Messaging/Client.hs b/src/Simplex/Messaging/Client.hs index 9a030d4a5..f2ed33af9 100644 --- a/src/Simplex/Messaging/Client.hs +++ b/src/Simplex/Messaging/Client.hs @@ -171,7 +171,7 @@ data PClient v err msg = PClient timeoutErrorCount :: TVar Int, clientCorrId :: TVar ChaChaDRG, sentCommands :: TMap CorrId (Request err msg), - sndQ :: TBQueue (Maybe (TVar Bool), ByteString), + sndQ :: TBQueue (Maybe (TVar Bool), ByteString, Maybe (Tag (ProtoCommand msg))), rcvQ :: TBQueue (NonEmpty (SignedTransmission err msg)), msgQ :: Maybe (TBQueue (ServerTransmissionBatch v err msg)) } @@ -563,9 +563,12 @@ getProtocolClient g transportSession@(_, srv, _) cfg@ProtocolClientConfig {qSize send :: Transport c => ProtocolClient v err msg -> THandle v c 'TClient -> IO () send ProtocolClient {client_ = PClient {sndQ}} h = forever $ atomically (readTBQueue sndQ) >>= sendPending where - sendPending (Nothing, s) = send_ s - sendPending (Just pending, s) = whenM (readTVarIO pending) $ send_ s - send_ = void . tPutLog h + sendPending (Nothing, s, tag) = send_ s tag + sendPending (Just pending, s, tag) = whenM (readTVarIO pending) $ send_ s tag + send_ s tag = + tPutLog h s >>= \case + Right () -> pure () + Left e -> putStrLn $ "send error: " <> show tag <> ", " <> show e receive :: Transport c => ProtocolClient v err msg -> THandle v c 'TClient -> IO () receive ProtocolClient {client_ = PClient {rcvQ, lastReceived, timeoutErrorCount}} h = forever $ do @@ -585,7 +588,7 @@ getProtocolClient g transportSession@(_, srv, _) cfg@ProtocolClientConfig {qSize if remaining > 1_000_000 -- delay pings only for significant time then loop remaining else do - whenM (readTVarIO sendPings) $ void . runExceptT $ sendProtocolCommand c Nothing NoEntity (protocolPing @v @err @msg) + whenM (readTVarIO sendPings) $ void . runExceptT $ sendProtocolCommand c Nothing NoEntity (protocolPing @v @err @msg) Nothing -- sendProtocolCommand/getResponse updates counter for each command cnt <- readTVarIO timeoutErrorCount -- drop client when maxCnt of commands have timed out in sequence, but only after some time has passed after last received response @@ -707,7 +710,7 @@ createSMPQueue :: Bool -> ExceptT SMPClientError IO QueueIdsKeys createSMPQueue c (rKey, rpKey) dhKey auth subMode sndSecure = - sendSMPCommand c (Just rpKey) NoEntity (NEW rKey dhKey auth subMode sndSecure) >>= \case + sendSMPCommand c (Just rpKey) NoEntity (NEW rKey dhKey auth subMode sndSecure) (Just NEW_) >>= \case IDS qik -> pure qik r -> throwE $ unexpectedResponse r @@ -717,7 +720,7 @@ createSMPQueue c (rKey, rpKey) dhKey auth subMode sndSecure = subscribeSMPQueue :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> ExceptT SMPClientError IO () subscribeSMPQueue c rpKey rId = do liftIO $ enablePings c - sendSMPCommand c (Just rpKey) rId SUB >>= \case + sendSMPCommand c (Just rpKey) rId SUB (Just SUB_) >>= \case OK -> pure () cmd@MSG {} -> liftIO $ writeSMPMessage c rId cmd r -> throwE $ unexpectedResponse r @@ -755,7 +758,7 @@ serverTransmission ProtocolClient {thParams = THandleParams {thVersion, sessionI -- https://github.covm/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#receive-a-message-from-the-queue getSMPMessage :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> ExceptT SMPClientError IO (Maybe RcvMessage) getSMPMessage c rpKey rId = - sendSMPCommand c (Just rpKey) rId GET >>= \case + sendSMPCommand c (Just rpKey) rId GET (Just GET_) >>= \case OK -> pure Nothing cmd@(MSG msg) -> liftIO (writeSMPMessage c rId cmd) $> Just msg r -> throwE $ unexpectedResponse r @@ -766,7 +769,7 @@ getSMPMessage c rpKey rId = subscribeSMPQueueNotifications :: SMPClient -> NtfPrivateAuthKey -> NotifierId -> ExceptT SMPClientError IO () subscribeSMPQueueNotifications c npKey nId = do liftIO $ enablePings c - okSMPCommand NSUB c npKey nId + okSMPCommand NSUB (Just NSUB_) c npKey nId {-# INLINE subscribeSMPQueueNotifications #-} -- | Subscribe to multiple SMP queues notifications batching commands if supported. @@ -784,12 +787,12 @@ enablePings ProtocolClient {client_ = PClient {sendPings}} = atomically $ writeT -- -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#secure-queue-command secureSMPQueue :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> SndPublicAuthKey -> ExceptT SMPClientError IO () -secureSMPQueue c rpKey rId senderKey = okSMPCommand (KEY senderKey) c rpKey rId +secureSMPQueue c rpKey rId senderKey = okSMPCommand (KEY senderKey) (Just KEY_) c rpKey rId {-# INLINE secureSMPQueue #-} -- | Secure the SMP queue via sender queue ID. secureSndSMPQueue :: SMPClient -> SndPrivateAuthKey -> SenderId -> SndPublicAuthKey -> ExceptT SMPClientError IO () -secureSndSMPQueue c spKey sId senderKey = okSMPCommand (SKEY senderKey) c spKey sId +secureSndSMPQueue c spKey sId senderKey = okSMPCommand (SKEY senderKey) (Just SKEY_) c spKey sId {-# INLINE secureSndSMPQueue #-} proxySecureSndSMPQueue :: SMPClient -> ProxiedRelay -> SndPrivateAuthKey -> SenderId -> SndPublicAuthKey -> ExceptT SMPClientError IO (Either ProxyClientError ()) @@ -801,7 +804,7 @@ proxySecureSndSMPQueue c proxiedRelay spKey sId senderKey = proxySMPCommand c pr -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#enable-notifications-command enableSMPQueueNotifications :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> NtfPublicAuthKey -> RcvNtfPublicDhKey -> ExceptT SMPClientError IO (NotifierId, RcvNtfPublicDhKey) enableSMPQueueNotifications c rpKey rId notifierKey rcvNtfPublicDhKey = - sendSMPCommand c (Just rpKey) rId (NKEY notifierKey rcvNtfPublicDhKey) >>= \case + sendSMPCommand c (Just rpKey) rId (NKEY notifierKey rcvNtfPublicDhKey) (Just NKEY_) >>= \case NID nId rcvNtfSrvPublicDhKey -> pure (nId, rcvNtfSrvPublicDhKey) r -> throwE $ unexpectedResponse r @@ -819,7 +822,7 @@ enableSMPQueuesNtfs c qs = L.map process <$> sendProtocolCommands c cs -- -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#disable-notifications-command disableSMPQueueNotifications :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> ExceptT SMPClientError IO () -disableSMPQueueNotifications = okSMPCommand NDEL +disableSMPQueueNotifications = okSMPCommand NDEL (Just NDEL_) {-# INLINE disableSMPQueueNotifications #-} -- | Disable notifications for multiple queues for push notifications server. @@ -832,7 +835,7 @@ disableSMPQueuesNtfs = okSMPCommands NDEL -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#send-message sendSMPMessage :: SMPClient -> Maybe SndPrivateAuthKey -> SenderId -> MsgFlags -> MsgBody -> ExceptT SMPClientError IO () sendSMPMessage c spKey sId flags msg = - sendSMPCommand c spKey sId (SEND flags msg) >>= \case + sendSMPCommand c spKey sId (SEND flags msg) (Just SEND_) >>= \case OK -> pure () r -> throwE $ unexpectedResponse r @@ -844,7 +847,7 @@ proxySMPMessage c proxiedRelay spKey sId flags msg = proxySMPCommand c proxiedRe -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#acknowledge-message-delivery ackSMPMessage :: SMPClient -> RcvPrivateAuthKey -> QueueId -> MsgId -> ExceptT SMPClientError IO () ackSMPMessage c rpKey rId msgId = - sendSMPCommand c (Just rpKey) rId (ACK msgId) >>= \case + sendSMPCommand c (Just rpKey) rId (ACK msgId) (Just ACK_) >>= \case OK -> return () cmd@MSG {} -> liftIO $ writeSMPMessage c rId cmd r -> throwE $ unexpectedResponse r @@ -854,14 +857,14 @@ ackSMPMessage c rpKey rId msgId = -- -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#suspend-queue suspendSMPQueue :: SMPClient -> RcvPrivateAuthKey -> QueueId -> ExceptT SMPClientError IO () -suspendSMPQueue = okSMPCommand OFF +suspendSMPQueue = okSMPCommand OFF (Just OFF_) {-# INLINE suspendSMPQueue #-} -- | Irreversibly delete SMP queue and all messages in it. -- -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#delete-queue deleteSMPQueue :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> ExceptT SMPClientError IO () -deleteSMPQueue = okSMPCommand DEL +deleteSMPQueue = okSMPCommand DEL (Just DEL_) {-# INLINE deleteSMPQueue #-} -- | Delete multiple SMP queues batching commands if supported. @@ -874,7 +877,7 @@ deleteSMPQueues = okSMPCommands DEL connectSMPProxiedRelay :: SMPClient -> SMPServer -> Maybe BasicAuth -> ExceptT SMPClientError IO ProxiedRelay connectSMPProxiedRelay c@ProtocolClient {client_ = PClient {tcpConnectTimeout, tcpTimeout}} relayServ@ProtocolServer {keyHash = C.KeyHash kh} proxyAuth | thVersion (thParams c) >= sendingProxySMPVersion = - sendProtocolCommand_ c Nothing tOut Nothing NoEntity (Cmd SProxiedClient (PRXY relayServ proxyAuth)) >>= \case + sendProtocolCommand_ c Nothing tOut Nothing NoEntity (Cmd SProxiedClient (PRXY relayServ proxyAuth)) (Just $ CT SProxiedClient PRXY_) >>= \case PKEY sId vr (chain, key) -> case supportedClientSMPRelayVRange `compatibleVersion` vr of Nothing -> throwE $ transportErr TEVersion @@ -976,7 +979,7 @@ proxySMPCommand c@ProtocolClient {thParams = proxyThParams, client_ = PClient {c et <- liftEitherWith PCECryptoError $ EncTransmission <$> C.cbEncrypt cmdSecret nonce b paddedProxiedTLength -- proxy interaction errors are wrapped let tOut = Just $ 2 * tcpTimeout - tryE (sendProtocolCommand_ c (Just nonce) tOut Nothing (EntityId sessionId) (Cmd SProxiedClient (PFWD v cmdPubKey et))) >>= \case + tryE (sendProtocolCommand_ c (Just nonce) tOut Nothing (EntityId sessionId) (Cmd SProxiedClient (PFWD v cmdPubKey et)) (Just $ CT SProxiedClient PFWD_)) >>= \case Right r -> case r of PRES (EncResponse er) -> do -- server interaction errors are thrown directly @@ -1012,7 +1015,7 @@ forwardSMPTransmission c@ProtocolClient {thParams, client_ = PClient {clientCorr let fwdT = FwdTransmission {fwdCorrId, fwdVersion, fwdKey, fwdTransmission} eft = EncFwdTransmission $ C.cbEncryptNoPad sessSecret nonce (smpEncode fwdT) -- send - sendProtocolCommand_ c (Just nonce) Nothing Nothing NoEntity (Cmd SSender (RFWD eft)) >>= \case + sendProtocolCommand_ c (Just nonce) Nothing Nothing NoEntity (Cmd SSender (RFWD eft)) (Just $ CT SSender RFWD_) >>= \case RRES (EncFwdResponse efr) -> do -- unwrap r' <- liftEitherWith PCECryptoError $ C.cbDecryptNoPad sessSecret (C.reverseNonce nonce) efr @@ -1022,13 +1025,13 @@ forwardSMPTransmission c@ProtocolClient {thParams, client_ = PClient {clientCorr getSMPQueueInfo :: SMPClient -> C.APrivateAuthKey -> QueueId -> ExceptT SMPClientError IO QueueInfo getSMPQueueInfo c pKey qId = - sendSMPCommand c (Just pKey) qId QUE >>= \case + sendSMPCommand c (Just pKey) qId QUE (Just QUE_) >>= \case INFO info -> pure info r -> throwE $ unexpectedResponse r -okSMPCommand :: PartyI p => Command p -> SMPClient -> C.APrivateAuthKey -> QueueId -> ExceptT SMPClientError IO () -okSMPCommand cmd c pKey qId = - sendSMPCommand c (Just pKey) qId cmd >>= \case +okSMPCommand :: PartyI p => Command p -> Maybe (CommandTag p) -> SMPClient -> C.APrivateAuthKey -> QueueId -> ExceptT SMPClientError IO () +okSMPCommand cmd cmdTag c pKey qId = + sendSMPCommand c (Just pKey) qId cmd cmdTag >>= \case OK -> return () r -> throwE $ unexpectedResponse r @@ -1043,8 +1046,8 @@ okSMPCommands cmd c qs = L.map process <$> sendProtocolCommands c cs Left e -> Left e -- | Send SMP command -sendSMPCommand :: PartyI p => SMPClient -> Maybe C.APrivateAuthKey -> QueueId -> Command p -> ExceptT SMPClientError IO BrokerMsg -sendSMPCommand c pKey qId cmd = sendProtocolCommand c pKey qId (Cmd sParty cmd) +sendSMPCommand :: PartyI p => SMPClient -> Maybe C.APrivateAuthKey -> QueueId -> Command p -> Maybe (CommandTag p) -> ExceptT SMPClientError IO BrokerMsg +sendSMPCommand c pKey qId cmd cmdTag = sendProtocolCommand c pKey qId (Cmd sParty cmd) (CT sParty <$> cmdTag) {-# INLINE sendSMPCommand #-} type PCTransmission err msg = (Either TransportError SentRawTransmission, Request err msg) @@ -1080,23 +1083,23 @@ sendBatch c@ProtocolClient {client_ = PClient {sndQ}} b = do pure [Response entityId $ Left $ PCETransportError e] TBTransmissions s n rs | n > 0 -> do - atomically $ writeTBQueue sndQ (Nothing, s) -- do not expire batched responses + atomically $ writeTBQueue sndQ (Nothing, s, Nothing) -- do not expire batched responses mapConcurrently (getResponse c Nothing) rs | otherwise -> pure [] TBTransmission s r -> do - atomically $ writeTBQueue sndQ (Nothing, s) + atomically $ writeTBQueue sndQ (Nothing, s, Nothing) (: []) <$> getResponse c Nothing r -- | Send Protocol command -sendProtocolCommand :: forall v err msg. Protocol v err msg => ProtocolClient v err msg -> Maybe C.APrivateAuthKey -> EntityId -> ProtoCommand msg -> ExceptT (ProtocolClientError err) IO msg +sendProtocolCommand :: forall v err msg. Protocol v err msg => ProtocolClient v err msg -> Maybe C.APrivateAuthKey -> EntityId -> ProtoCommand msg -> Maybe (Tag (ProtoCommand msg)) -> ExceptT (ProtocolClientError err) IO msg sendProtocolCommand c = sendProtocolCommand_ c Nothing Nothing -- Currently there is coupling - batch commands do not expire, and individually sent commands do. -- This is to reflect the fact that we send subscriptions only as batches, and also because we do not track a separate timeout for the whole batch, so it is not obvious when should we expire it. -- We could expire a batch of deletes, for example, either when the first response expires or when the last one does. -- But a better solution is to process delayed delete responses. -sendProtocolCommand_ :: forall v err msg. Protocol v err msg => ProtocolClient v err msg -> Maybe C.CbNonce -> Maybe Int -> Maybe C.APrivateAuthKey -> EntityId -> ProtoCommand msg -> ExceptT (ProtocolClientError err) IO msg -sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THandleParams {batch, blockSize}} nonce_ tOut pKey entId cmd = +sendProtocolCommand_ :: forall v err msg. Protocol v err msg => ProtocolClient v err msg -> Maybe C.CbNonce -> Maybe Int -> Maybe C.APrivateAuthKey -> EntityId -> ProtoCommand msg -> Maybe (Tag (ProtoCommand msg)) -> ExceptT (ProtocolClientError err) IO msg +sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THandleParams {batch, blockSize}} nonce_ tOut pKey entId cmd cmdTag = ExceptT $ uncurry sendRecv =<< mkTransmission_ c nonce_ (pKey, entId, cmd) where -- two separate "atomically" needed to avoid blocking @@ -1106,7 +1109,7 @@ sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THan Right t | B.length s > blockSize - 2 -> pure . Left $ PCETransportError TELargeMsg | otherwise -> do - atomically $ writeTBQueue sndQ (Just pending, s) + atomically $ writeTBQueue sndQ (Just pending, s, cmdTag) response <$> getResponse c tOut r where s diff --git a/src/Simplex/Messaging/Notifications/Client.hs b/src/Simplex/Messaging/Notifications/Client.hs index 273010c2c..af80474a2 100644 --- a/src/Simplex/Messaging/Notifications/Client.hs +++ b/src/Simplex/Messaging/Notifications/Client.hs @@ -87,7 +87,7 @@ ntfDeleteSubscription = okNtfCommand SDEL -- | Send notification server command sendNtfCommand :: NtfEntityI e => NtfClient -> Maybe C.APrivateAuthKey -> NtfEntityId -> NtfCommand e -> ExceptT NtfClientError IO NtfResponse -sendNtfCommand c pKey entId cmd = sendProtocolCommand c pKey entId (NtfCmd sNtfEntity cmd) +sendNtfCommand c pKey entId cmd = sendProtocolCommand c pKey entId (NtfCmd sNtfEntity cmd) Nothing okNtfCommand :: NtfEntityI e => NtfCommand e -> NtfClient -> C.APrivateAuthKey -> NtfEntityId -> ExceptT NtfClientError IO () okNtfCommand cmd c pKey entId = diff --git a/src/Simplex/Messaging/Notifications/Protocol.hs b/src/Simplex/Messaging/Notifications/Protocol.hs index 96f8b337e..914f4df3c 100644 --- a/src/Simplex/Messaging/Notifications/Protocol.hs +++ b/src/Simplex/Messaging/Notifications/Protocol.hs @@ -80,6 +80,8 @@ deriving instance Show (NtfCommandTag e) data NtfCmdTag = forall e. NtfEntityI e => NCT (SNtfEntity e) (NtfCommandTag e) +deriving instance Show NtfCmdTag + instance NtfEntityI e => Encoding (NtfCommandTag e) where smpEncode = \case TNEW_ -> "TNEW" diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index fd36d2911..57a438a2c 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -60,6 +60,7 @@ module Simplex.Messaging.Protocol SenderCanSecure, Party (..), Cmd (..), + CmdTag (..), DirectParty, BrokerMsg (..), SParty (..), @@ -1358,7 +1359,7 @@ instance Protocol SMPVersion ErrorType BrokerMsg where ERR e -> Just e _ -> Nothing -class ProtocolMsgTag (Tag msg) => ProtocolEncoding v err msg | msg -> err, msg -> v where +class (ProtocolMsgTag (Tag msg), Show (Tag msg)) => ProtocolEncoding v err msg | msg -> err, msg -> v where type Tag msg encodeProtocol :: Version v -> msg -> ByteString protocolP :: Version v -> Tag msg -> Parser msg From 5e1ba8d21c1314968719f5dd4839299a0da9b254 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Jan 2025 14:15:12 +0000 Subject: [PATCH 10/19] log error and client version --- simplexmq.cabal | 2 +- src/Simplex/Messaging/Client.hs | 16 +++++++++------- src/Simplex/Messaging/Protocol.hs | 2 +- src/Simplex/Messaging/Server.hs | 19 +++++++++++++++++++ 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/simplexmq.cabal b/simplexmq.cabal index 9af356628..34b0bdc59 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -1,7 +1,7 @@ cabal-version: 1.12 name: simplexmq -version: 6.3.0.102 +version: 6.3.0.103 synopsis: SimpleXMQ message broker description: This package includes <./docs/Simplex-Messaging-Server.html server>, <./docs/Simplex-Messaging-Client.html client> and diff --git a/src/Simplex/Messaging/Client.hs b/src/Simplex/Messaging/Client.hs index f2ed33af9..be359b71b 100644 --- a/src/Simplex/Messaging/Client.hs +++ b/src/Simplex/Messaging/Client.hs @@ -171,7 +171,7 @@ data PClient v err msg = PClient timeoutErrorCount :: TVar Int, clientCorrId :: TVar ChaChaDRG, sentCommands :: TMap CorrId (Request err msg), - sndQ :: TBQueue (Maybe (TVar Bool), ByteString, Maybe (Tag (ProtoCommand msg))), + sndQ :: TBQueue (Maybe (Request err msg), ByteString, Maybe (Tag (ProtoCommand msg))), rcvQ :: TBQueue (NonEmpty (SignedTransmission err msg)), msgQ :: Maybe (TBQueue (ServerTransmissionBatch v err msg)) } @@ -563,12 +563,14 @@ getProtocolClient g transportSession@(_, srv, _) cfg@ProtocolClientConfig {qSize send :: Transport c => ProtocolClient v err msg -> THandle v c 'TClient -> IO () send ProtocolClient {client_ = PClient {sndQ}} h = forever $ atomically (readTBQueue sndQ) >>= sendPending where - sendPending (Nothing, s, tag) = send_ s tag - sendPending (Just pending, s, tag) = whenM (readTVarIO pending) $ send_ s tag - send_ s tag = + sendPending (Nothing, s, tag) = send_ s tag Nothing + sendPending (Just Request {pending, responseVar}, s, tag) = whenM (readTVarIO pending) $ send_ s tag (Just responseVar) + send_ s tag responseVar_ = tPutLog h s >>= \case Right () -> pure () - Left e -> putStrLn $ "send error: " <> show tag <> ", " <> show e + Left e -> do + putStrLn $ "send error: " <> show tag <> ", " <> show e + atomically $ forM_ responseVar_ $ \v -> putTMVar v $ Left $ PCETransportError e receive :: Transport c => ProtocolClient v err msg -> THandle v c 'TClient -> IO () receive ProtocolClient {client_ = PClient {rcvQ, lastReceived, timeoutErrorCount}} h = forever $ do @@ -1104,12 +1106,12 @@ sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THan where -- two separate "atomically" needed to avoid blocking sendRecv :: Either TransportError SentRawTransmission -> Request err msg -> IO (Either (ProtocolClientError err) msg) - sendRecv t_ r@Request {pending} = case t_ of + sendRecv t_ r = case t_ of Left e -> pure . Left $ PCETransportError e Right t | B.length s > blockSize - 2 -> pure . Left $ PCETransportError TELargeMsg | otherwise -> do - atomically $ writeTBQueue sndQ (Just pending, s, cmdTag) + atomically $ writeTBQueue sndQ (Just r, s, cmdTag) response <$> getResponse c tOut r where s diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index 57a438a2c..5741cbb66 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -1703,7 +1703,7 @@ tPutLog th@THandle {params} s = do pure r where paramsStr = - ", block size" + ", block size " <> show (B.length s) <> ", thServerVRange = " <> show (thServerVRange params) diff --git a/src/Simplex/Messaging/Server.hs b/src/Simplex/Messaging/Server.hs index d31a50e34..13c140cd4 100644 --- a/src/Simplex/Messaging/Server.hs +++ b/src/Simplex/Messaging/Server.hs @@ -1217,6 +1217,25 @@ client Right r -> PRES r <$ inc own pSuccesses Left e -> ERR (smpProxyError e) <$ case e of PCEProtocolError {} -> inc own pSuccesses + PCETransportError e' -> do + liftIO $ putStrLn $ "forwardSMPTransmission error: " <> show e' <> paramsStr + inc own pErrorsOther + where + THandleParams {thVersion = v'} = thParams' + EncTransmission s = encBlock + paramsStr = + ", block size " + <> show (B.length s) + <> ", thServerVRange = " + <> show (thServerVRange thParams') + <> ", thVersion = " + <> show v' + <> ", thAuth = " + <> show (isJust $ thAuth thParams') + <> ", implySessId = " + <> show (implySessId thParams') + <> ", encryptBlock = " + <> show (isJust $ encryptBlock thParams') _ -> inc own pErrorsOther else Just (ERR $ transportErr TEVersion) <$ inc own pErrorsCompat where From b8830242e570e6b6600fe4c874789994891354a5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Jan 2025 21:42:04 +0000 Subject: [PATCH 11/19] cap proxy version for previous destination server --- src/Simplex/FileTransfer/Transport.hs | 4 +- src/Simplex/Messaging/Client.hs | 7 +- .../Messaging/Notifications/Transport.hs | 4 +- src/Simplex/Messaging/Protocol.hs | 4 +- src/Simplex/Messaging/Server/Main.hs | 1 + src/Simplex/Messaging/Transport.hs | 111 ++++++++++-------- tests/CLITests.hs | 2 +- tests/NtfClient.hs | 2 +- tests/SMPClient.hs | 4 +- tests/SMPProxyTests.hs | 65 +++++----- 10 files changed, 114 insertions(+), 90 deletions(-) diff --git a/src/Simplex/FileTransfer/Transport.hs b/src/Simplex/FileTransfer/Transport.hs index 7f90b2879..3d80949d0 100644 --- a/src/Simplex/FileTransfer/Transport.hs +++ b/src/Simplex/FileTransfer/Transport.hs @@ -102,8 +102,8 @@ supportedFileServerVRange :: VersionRangeXFTP supportedFileServerVRange = mkVersionRange initialXFTPVersion currentXFTPVersion -- XFTP protocol does not use this handshake method -xftpClientHandshakeStub :: c -> Maybe C.KeyPairX25519 -> C.KeyHash -> VersionRangeXFTP -> ExceptT TransportError IO (THandle XFTPVersion c 'TClient) -xftpClientHandshakeStub _c _ks _keyHash _xftpVRange = throwE TEVersion +xftpClientHandshakeStub :: c -> Maybe C.KeyPairX25519 -> C.KeyHash -> VersionRangeXFTP -> Bool -> ExceptT TransportError IO (THandle XFTPVersion c 'TClient) +xftpClientHandshakeStub _c _ks _keyHash _xftpVRange _proxyServer = throwE TEVersion supportedXFTPhandshakes :: [ALPN] supportedXFTPhandshakes = ["xftp/1"] diff --git a/src/Simplex/Messaging/Client.hs b/src/Simplex/Messaging/Client.hs index be359b71b..386e22c23 100644 --- a/src/Simplex/Messaging/Client.hs +++ b/src/Simplex/Messaging/Client.hs @@ -406,6 +406,8 @@ data ProtocolClientConfig v = ProtocolClientConfig serverVRange :: VersionRange v, -- | agree shared session secret (used in SMP proxy for additional encryption layer) agreeSecret :: Bool, + -- | Whether connecting client is a proxy server. See comment in ClientHandshake + proxyServer :: Bool, -- | send SNI to server, False for SMP useSNI :: Bool } @@ -420,6 +422,7 @@ defaultClientConfig clientALPN useSNI serverVRange = clientALPN, serverVRange, agreeSecret = False, + proxyServer = False, useSNI } {-# INLINE defaultClientConfig #-} @@ -489,7 +492,7 @@ type TransportSession msg = (UserId, ProtoServer msg, Maybe ByteString) -- A single queue can be used for multiple 'SMPClient' instances, -- as 'SMPServerTransmission' includes server information. getProtocolClient :: forall v err msg. Protocol v err msg => TVar ChaChaDRG -> TransportSession msg -> ProtocolClientConfig v -> Maybe (TBQueue (ServerTransmissionBatch v err msg)) -> UTCTime -> (ProtocolClient v err msg -> IO ()) -> IO (Either (ProtocolClientError err) (ProtocolClient v err msg)) -getProtocolClient g transportSession@(_, srv, _) cfg@ProtocolClientConfig {qSize, networkConfig, clientALPN, serverVRange, agreeSecret, useSNI} msgQ proxySessTs disconnected = do +getProtocolClient g transportSession@(_, srv, _) cfg@ProtocolClientConfig {qSize, networkConfig, clientALPN, serverVRange, agreeSecret, proxyServer, useSNI} msgQ proxySessTs disconnected = do case chooseTransportHost networkConfig (host srv) of Right useHost -> (getCurrentTime >>= mkProtocolClient useHost >>= runClient useTransport useHost) @@ -548,7 +551,7 @@ getProtocolClient g transportSession@(_, srv, _) cfg@ProtocolClientConfig {qSize client :: forall c. Transport c => TProxy c -> PClient v err msg -> TMVar (Either (ProtocolClientError err) (ProtocolClient v err msg)) -> c -> IO () client _ c cVar h = do ks <- if agreeSecret then Just <$> atomically (C.generateKeyPair g) else pure Nothing - runExceptT (protocolClientHandshake @v @err @msg h ks (keyHash srv) serverVRange) >>= \case + runExceptT (protocolClientHandshake @v @err @msg h ks (keyHash srv) serverVRange proxyServer) >>= \case Left e -> atomically . putTMVar cVar . Left $ PCETransportError e Right th@THandle {params} -> do sessionTs <- getCurrentTime diff --git a/src/Simplex/Messaging/Notifications/Transport.hs b/src/Simplex/Messaging/Notifications/Transport.hs index 836b6b1e7..fc928535d 100644 --- a/src/Simplex/Messaging/Notifications/Transport.hs +++ b/src/Simplex/Messaging/Notifications/Transport.hs @@ -123,8 +123,8 @@ ntfServerHandshake serverSignKey c (k, pk) kh ntfVRange = do Nothing -> throwE TEVersion -- | Notifcations server client transport handshake. -ntfClientHandshake :: forall c. Transport c => c -> C.KeyHash -> VersionRangeNTF -> ExceptT TransportError IO (THandleNTF c 'TClient) -ntfClientHandshake c keyHash ntfVRange = do +ntfClientHandshake :: forall c. Transport c => c -> C.KeyHash -> VersionRangeNTF -> Bool -> ExceptT TransportError IO (THandleNTF c 'TClient) +ntfClientHandshake c keyHash ntfVRange _proxyServer = do let th@THandle {params = THandleParams {sessionId}} = ntfTHandle c NtfServerHandshake {sessionId = sessId, ntfVersionRange, authPubKey = sk'} <- getHandshake th if sessionId /= sessId diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index 5741cbb66..2d54629b4 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -1344,7 +1344,7 @@ transmissionP THandleParams {sessionId, implySessId} = do class (ProtocolTypeI (ProtoType msg), ProtocolEncoding v err msg, ProtocolEncoding v err (ProtoCommand msg), Show err, Show msg) => Protocol v err msg | msg -> v, msg -> err where type ProtoCommand msg = cmd | cmd -> msg type ProtoType msg = (sch :: ProtocolType) | sch -> msg - protocolClientHandshake :: forall c. Transport c => c -> Maybe C.KeyPairX25519 -> C.KeyHash -> VersionRange v -> ExceptT TransportError IO (THandle v c 'TClient) + protocolClientHandshake :: forall c. Transport c => c -> Maybe C.KeyPairX25519 -> C.KeyHash -> VersionRange v -> Bool -> ExceptT TransportError IO (THandle v c 'TClient) protocolPing :: ProtoCommand msg protocolError :: msg -> Maybe err @@ -1492,7 +1492,7 @@ instance ProtocolEncoding SMPVersion ErrorType BrokerMsg where INFO info -> e (INFO_, ' ', info) OK -> e OK_ ERR err -> case err of - BLOCKED _ | v < blockedEntityErrorSMPVersion -> e (ERR_, ' ', AUTH) + BLOCKED _ | v < blockedEntitySMPVersion -> e (ERR_, ' ', AUTH) _ -> e (ERR_, ' ', err) PONG -> e PONG_ where diff --git a/src/Simplex/Messaging/Server/Main.hs b/src/Simplex/Messaging/Server/Main.hs index 1ceb64d6e..1d21ffa6a 100644 --- a/src/Simplex/Messaging/Server/Main.hs +++ b/src/Simplex/Messaging/Server/Main.hs @@ -448,6 +448,7 @@ smpServerCLI_ generateSite serveStaticFiles attachStaticFiles cfgPath logPath = (smpCfg defaultSMPClientAgentConfig) { serverVRange = supportedProxyClientSMPRelayVRange, agreeSecret = True, + proxyServer = True, networkConfig = defaultNetworkConfig { socksProxy = either error id <$!> strDecodeIni "PROXY" "socks_proxy" ini, diff --git a/src/Simplex/Messaging/Transport.hs b/src/Simplex/Messaging/Transport.hs index 62f42a17d..d537b45c2 100644 --- a/src/Simplex/Messaging/Transport.hs +++ b/src/Simplex/Messaging/Transport.hs @@ -3,6 +3,7 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE KindSignatures #-} @@ -14,6 +15,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} -- | -- Module : Simplex.Messaging.Transport @@ -49,7 +51,7 @@ module Simplex.Messaging.Transport sndAuthKeySMPVersion, deletedEventSMPVersion, encryptedBlockSMPVersion, - blockedEntityErrorSMPVersion, + blockedEntitySMPVersion, simplexMQVersion, smpBlockSize, TransportConfig (..), @@ -90,7 +92,7 @@ where import Control.Applicative (optional) import Control.Concurrent.STM -import Control.Monad (forM, (<$!>)) +import Control.Monad (forM, when, (<$!>)) import Control.Monad.Except import Control.Monad.IO.Class import Control.Monad.Trans.Except (throwE) @@ -139,11 +141,11 @@ smpBlockSize = 16384 -- 5 - basic auth for SMP servers (11/12/2022) -- 6 - allow creating queues without subscribing (9/10/2023) -- 7 - support authenticated encryption to verify senders' commands, imply but do NOT send session ID in signed part (4/30/2024) --- 8 - SMP proxy for sender commands --- 9 - faster handshake: SKEY command for sender to secure queue --- 10 - DELD event to subscriber when queue is deleted via another connnection --- 11 - additional encryption of transport blocks with forward secrecy (9/14/2024) --- 12 - BLOCKED error for blocked queues (1/11/2025) +-- 8 - SMP proxy for sender commands (6/03/2024) +-- 9 - faster handshake: SKEY command for sender to secure queue (6/30/2024) +-- 10 - DELD event to subscriber when queue is deleted via another connnection (9/11/2024) +-- 11 - additional encryption of transport blocks with forward secrecy (10/06/2024) +-- 12 - BLOCKED error for blocked queues, handshake property to disable transport encryption between server and proxy (1/11/2025) data SMPVersion @@ -174,8 +176,8 @@ deletedEventSMPVersion = VersionSMP 10 encryptedBlockSMPVersion :: VersionSMP encryptedBlockSMPVersion = VersionSMP 11 -blockedEntityErrorSMPVersion :: VersionSMP -blockedEntityErrorSMPVersion = VersionSMP 12 +blockedEntitySMPVersion :: VersionSMP +blockedEntitySMPVersion = VersionSMP 12 minClientSMPRelayVersion :: VersionSMP minClientSMPRelayVersion = VersionSMP 6 @@ -200,7 +202,7 @@ currentServerSMPRelayVersion = VersionSMP 12 proxiedSMPRelayVersion :: VersionSMP proxiedSMPRelayVersion = VersionSMP 12 --- minimal supported protocol version is 4 +-- minimal supported protocol version is 6 -- TODO remove code that supports sending commands without batching supportedClientSMPRelayVRange :: VersionRangeSMP supportedClientSMPRelayVRange = mkVersionRange minClientSMPRelayVersion currentClientSMPRelayVersion @@ -416,7 +418,7 @@ data THandleParams v p = THandleParams -- | do NOT send session ID in transmission, but include it into signed message -- based on protocol version implySessId :: Bool, - -- -- | additional block encryption + -- | keys for additional transport encryption encryptBlock :: Maybe TSbChainKeys, -- | send multiple transmissions in a single block -- based on protocol version @@ -457,18 +459,28 @@ data ClientHandshake = ClientHandshake smpVersion :: VersionSMP, -- | server identity - CA certificate fingerprint keyHash :: C.KeyHash, - -- pub key to agree shared secret for entity ID encryption, shared secret for command authorization is agreed using per-queue keys. - authPubKey :: Maybe C.PublicKeyX25519 + -- | pub key to agree shared secret for entity ID encryption, shared secret for command authorization is agreed using per-queue keys. + authPubKey :: Maybe C.PublicKeyX25519, + -- | Whether connecting client is a proxy server (send from SMP v12). + -- This property, if True, disables additional transport encrytion inside TLS. + -- (Proxy server connection already has additional encryption, so this layer is not needed there). + proxyServer :: Bool } instance Encoding ClientHandshake where - smpEncode ClientHandshake {smpVersion, keyHash, authPubKey} = - smpEncode (smpVersion, keyHash) <> encodeAuthEncryptCmds smpVersion authPubKey + smpEncode ClientHandshake {smpVersion = v, keyHash, authPubKey, proxyServer} = + smpEncode (v, keyHash) + <> encodeAuthEncryptCmds v authPubKey + <> ifHasProxy v (smpEncode proxyServer) "" smpP = do - (smpVersion, keyHash) <- smpP + (v, keyHash) <- smpP -- TODO drop SMP v6: remove special parser and make key non-optional - authPubKey <- authEncryptCmdsP smpVersion smpP - pure ClientHandshake {smpVersion, keyHash, authPubKey} + authPubKey <- authEncryptCmdsP v smpP + proxyServer <- ifHasProxy v smpP (pure False) + pure ClientHandshake {smpVersion = v, keyHash, authPubKey, proxyServer} + +ifHasProxy :: VersionSMP -> a -> a -> a +ifHasProxy v a b = if v >= blockedEntitySMPVersion then a else b instance Encoding ServerHandshake where smpEncode ServerHandshake {smpVersionRange, sessionId, authPubKey} = @@ -576,54 +588,57 @@ smpServerHandshake serverSignKey c (k, pk) kh smpVRange = do smpVersionRange = maybe legacyServerSMPRelayVRange (const smpVRange) $ getSessionALPN c sendHandshake th $ ServerHandshake {sessionId, smpVersionRange, authPubKey = Just (certChain, sk)} getHandshake th >>= \case - ClientHandshake {smpVersion = v, keyHash, authPubKey = k'} + ClientHandshake {smpVersion = v, keyHash, authPubKey = k', proxyServer} | keyHash /= kh -> throwE $ TEHandshake IDENTITY | otherwise -> case compatibleVRange' smpVersionRange v of - Just (Compatible vr) -> liftIO $ smpTHandleServer th v vr pk k' + Just (Compatible vr) -> liftIO $ smpTHandleServer th v vr pk k' proxyServer Nothing -> throwE TEVersion -- | Client SMP transport handshake. -- -- See https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#appendix-a -smpClientHandshake :: forall c. Transport c => c -> Maybe C.KeyPairX25519 -> C.KeyHash -> VersionRangeSMP -> ExceptT TransportError IO (THandleSMP c 'TClient) -smpClientHandshake c ks_ keyHash@(C.KeyHash kh) smpVRange = do +smpClientHandshake :: forall c. Transport c => c -> Maybe C.KeyPairX25519 -> C.KeyHash -> VersionRangeSMP -> Bool -> ExceptT TransportError IO (THandleSMP c 'TClient) +smpClientHandshake c ks_ keyHash@(C.KeyHash kh) vRange proxyServer = do let th@THandle {params = THandleParams {sessionId}} = smpTHandle c ServerHandshake {sessionId = sessId, smpVersionRange, authPubKey} <- getHandshake th - if sessionId /= sessId - then throwE TEBadSession - else case smpVersionRange `compatibleVRange` smpVRange of - Just (Compatible vr) -> do - ck_ <- forM authPubKey $ \certKey@(X.CertificateChain cert, exact) -> - liftEitherWith (const $ TEHandshake BAD_AUTH) $ do - case cert of - [_leaf, ca] | XV.Fingerprint kh == XV.getFingerprint ca X.HashSHA256 -> pure () - _ -> throwError "bad certificate" - serverKey <- getServerVerifyKey c - pubKey <- C.verifyX509 serverKey exact - (,certKey) <$> (C.x509ToPublic (pubKey, []) >>= C.pubKey) - let v = maxVersion vr - sendHandshake th $ ClientHandshake {smpVersion = v, keyHash, authPubKey = fst <$> ks_} - liftIO $ smpTHandleClient th v vr (snd <$> ks_) ck_ - Nothing -> throwE TEVersion - -smpTHandleServer :: forall c. THandleSMP c 'TServer -> VersionSMP -> VersionRangeSMP -> C.PrivateKeyX25519 -> Maybe C.PublicKeyX25519 -> IO (THandleSMP c 'TServer) -smpTHandleServer th v vr pk k_ = do + when (sessionId /= sessId) $ throwE TEBadSession + let smpVRange = + if proxyServer && maxVersion smpVersionRange < blockedEntitySMPVersion + then vRange {maxVersion = max (minVersion vRange) deletedEventSMPVersion} + else vRange + case smpVersionRange `compatibleVRange` smpVRange of + Just (Compatible vr) -> do + ck_ <- forM authPubKey $ \certKey@(X.CertificateChain cert, exact) -> + liftEitherWith (const $ TEHandshake BAD_AUTH) $ do + case cert of + [_leaf, ca] | XV.Fingerprint kh == XV.getFingerprint ca X.HashSHA256 -> pure () + _ -> throwError "bad certificate" + serverKey <- getServerVerifyKey c + pubKey <- C.verifyX509 serverKey exact + (,certKey) <$> (C.x509ToPublic (pubKey, []) >>= C.pubKey) + let v = maxVersion vr + sendHandshake th $ ClientHandshake {smpVersion = v, keyHash, authPubKey = fst <$> ks_, proxyServer} + liftIO $ smpTHandleClient th v vr (snd <$> ks_) ck_ proxyServer + Nothing -> throwE TEVersion + +smpTHandleServer :: forall c. THandleSMP c 'TServer -> VersionSMP -> VersionRangeSMP -> C.PrivateKeyX25519 -> Maybe C.PublicKeyX25519 -> Bool -> IO (THandleSMP c 'TServer) +smpTHandleServer th v vr pk k_ proxyServer = do let thAuth = Just THAuthServer {serverPrivKey = pk, sessSecret' = (`C.dh'` pk) <$!> k_} - be <- blockEncryption th v thAuth + be <- blockEncryption th v proxyServer thAuth pure $ smpTHandle_ th v vr thAuth $ uncurry TSbChainKeys <$> be -smpTHandleClient :: forall c. THandleSMP c 'TClient -> VersionSMP -> VersionRangeSMP -> Maybe C.PrivateKeyX25519 -> Maybe (C.PublicKeyX25519, (X.CertificateChain, X.SignedExact X.PubKey)) -> IO (THandleSMP c 'TClient) -smpTHandleClient th v vr pk_ ck_ = do +smpTHandleClient :: forall c. THandleSMP c 'TClient -> VersionSMP -> VersionRangeSMP -> Maybe C.PrivateKeyX25519 -> Maybe (C.PublicKeyX25519, (X.CertificateChain, X.SignedExact X.PubKey)) -> Bool -> IO (THandleSMP c 'TClient) +smpTHandleClient th v vr pk_ ck_ proxyServer = do let thAuth = (\(k, ck) -> THAuthClient {serverPeerPubKey = k, serverCertKey = forceCertChain ck, sessSecret = C.dh' k <$!> pk_}) <$!> ck_ - be <- blockEncryption th v thAuth + be <- blockEncryption th v proxyServer thAuth -- swap is needed to use client's sndKey as server's rcvKey and vice versa pure $ smpTHandle_ th v vr thAuth $ uncurry TSbChainKeys . swap <$> be -blockEncryption :: THandleSMP c p -> VersionSMP -> Maybe (THandleAuth p) -> IO (Maybe (TVar C.SbChainKey, TVar C.SbChainKey)) -blockEncryption THandle {params = THandleParams {sessionId}} v = \case - Just thAuth | v >= encryptedBlockSMPVersion -> case thAuth of +blockEncryption :: THandleSMP c p -> VersionSMP -> Bool -> Maybe (THandleAuth p) -> IO (Maybe (TVar C.SbChainKey, TVar C.SbChainKey)) +blockEncryption THandle {params = THandleParams {sessionId}} v proxyServer = \case + Just thAuth | not proxyServer && v >= encryptedBlockSMPVersion -> case thAuth of THAuthClient {sessSecret} -> be sessSecret THAuthServer {sessSecret'} -> be sessSecret' _ -> pure Nothing diff --git a/tests/CLITests.hs b/tests/CLITests.hs index 01b68653d..10ec33f4c 100644 --- a/tests/CLITests.hs +++ b/tests/CLITests.hs @@ -174,7 +174,7 @@ smpServerTestStatic = do X.Certificate {X.certPubKey = X.PubKeyEd25519 _k} : _ca -> print _ca -- pure () leaf : _ -> error $ "Unexpected leaf cert: " <> show leaf [] -> error "Empty chain" - runRight_ . void $ smpClientHandshake tls Nothing caSMP supportedClientSMPRelayVRange + runRight_ . void $ smpClientHandshake tls Nothing caSMP supportedClientSMPRelayVRange False logDebug "Combined SMP works" where getCerts :: TLS -> [X.Certificate] diff --git a/tests/NtfClient.hs b/tests/NtfClient.hs index e8c263e89..190815832 100644 --- a/tests/NtfClient.hs +++ b/tests/NtfClient.hs @@ -82,7 +82,7 @@ testNtfClient :: Transport c => (THandleNTF c 'TClient -> IO a) -> IO a testNtfClient client = do Right host <- pure $ chooseTransportHost defaultNetworkConfig testHost runTransportClient defaultTransportClientConfig Nothing host ntfTestPort (Just testKeyHash) $ \h -> - runExceptT (ntfClientHandshake h testKeyHash supportedClientNTFVRange) >>= \case + runExceptT (ntfClientHandshake h testKeyHash supportedClientNTFVRange False) >>= \case Right th -> client th Left e -> error $ show e diff --git a/tests/SMPClient.hs b/tests/SMPClient.hs index a92b9e132..53d7a2fdc 100644 --- a/tests/SMPClient.hs +++ b/tests/SMPClient.hs @@ -105,7 +105,7 @@ testSMPClient_ :: Transport c => TransportHost -> ServiceName -> VersionRangeSMP testSMPClient_ host port vr client = do let tcConfig = defaultTransportClientConfig {Client.alpn = clientALPN} runTransportClient tcConfig Nothing host port (Just testKeyHash) $ \h -> - runExceptT (smpClientHandshake h Nothing testKeyHash vr) >>= \case + runExceptT (smpClientHandshake h Nothing testKeyHash vr False) >>= \case Right th -> client th Left e -> error $ show e where @@ -185,7 +185,7 @@ proxyCfg :: ServerConfig proxyCfg = cfg { allowSMPProxy = True, - smpAgentCfg = smpAgentCfg' {smpCfg = (smpCfg smpAgentCfg') {agreeSecret = True, serverVRange = supportedProxyClientSMPRelayVRange}} + smpAgentCfg = smpAgentCfg' {smpCfg = (smpCfg smpAgentCfg') {agreeSecret = True, proxyServer = True, serverVRange = supportedProxyClientSMPRelayVRange}} } where smpAgentCfg' = smpAgentCfg cfg diff --git a/tests/SMPProxyTests.hs b/tests/SMPProxyTests.hs index 929ab9e42..bc5091119 100644 --- a/tests/SMPProxyTests.hs +++ b/tests/SMPProxyTests.hs @@ -86,34 +86,36 @@ smpProxyTests = do deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 it "max message size, X25519 keys" . twoServersFirstProxy $ deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 - -- describe "new server and old proxy" $ do - -- let proxyServ = srv1 - -- relayServ = srv2 - -- (msg1, msg2) <- runIO $ do - -- g <- C.newRandom - -- atomically $ (,) <$> C.randomBytes maxLen g <*> C.randomBytes maxLen g - -- it "deliver via proxy" . twoServers_ proxyCfgV62 proxyCfg $ - -- deliverMessageViaProxy proxyServ relayServ C.SEd448 "hello 1" "hello 2" - -- it "max message size, Ed448 keys" . twoServers_ proxyCfgV62 proxyCfg $ - -- deliverMessageViaProxy proxyServ relayServ C.SEd448 msg1 msg2 - -- it "max message size, Ed25519 keys" . twoServers_ proxyCfgV62 proxyCfg $ - -- deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 - -- it "max message size, X25519 keys" . twoServers_ proxyCfgV62 proxyCfg $ - -- deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 - -- describe "old server and new proxy" $ do - -- let proxyServ = srv1 - -- relayServ = srv2 - -- (msg1, msg2) <- runIO $ do - -- g <- C.newRandom - -- atomically $ (,) <$> C.randomBytes maxLen g <*> C.randomBytes maxLen g - -- it "deliver via proxy" . twoServers_ proxyCfg proxyCfgV62 $ - -- deliverMessageViaProxy proxyServ relayServ C.SEd448 "hello 1" "hello 2" - -- it "max message size, Ed448 keys" . twoServers_ proxyCfg proxyCfgV62 $ - -- deliverMessageViaProxy proxyServ relayServ C.SEd448 msg1 msg2 - -- it "max message size, Ed25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ - -- deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 - -- it "max message size, X25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ - -- deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 + xdescribe "client without block encryption, server v6.2 and proxy v6.3 (fails)" $ do + let proxyServ = srv1 + relayServ = srv2 + maxLen' = maxMessageLength deletedEventSMPVersion + (msg1, msg2) <- runIO $ do + g <- C.newRandom + atomically $ (,) <$> C.randomBytes maxLen' g <*> C.randomBytes maxLen' g + it "deliver via proxy" . twoServers_ proxyCfg proxyCfgV62 $ + deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd448 ["hello 1"] ["hello 2"] + it "max message size, Ed448 keys" . twoServers_ proxyCfg proxyCfgV62 $ + deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd448 [msg1] [msg2] + it "max message size, Ed25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ + deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd25519 [msg1] [msg2] + it "max message size, X25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ + deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SX25519 [msg1] [msg2] + xdescribe "client without block encryption, server v6.2 and proxy v6.2 (works)" $ do + let proxyServ = srv1 + relayServ = srv2 + maxLen' = maxMessageLength deletedEventSMPVersion + (msg1, msg2) <- runIO $ do + g <- C.newRandom + atomically $ (,) <$> C.randomBytes maxLen' g <*> C.randomBytes maxLen' g + it "deliver via proxy" . twoServers_ proxyCfgV62 proxyCfgV62 $ + deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd448 ["hello 1"] ["hello 2"] + it "max message size, Ed448 keys" . twoServers_ proxyCfgV62 proxyCfgV62 $ + deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd448 [msg1] [msg2] + it "max message size, Ed25519 keys" . twoServers_ proxyCfgV62 proxyCfgV62 $ + deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd25519 [msg1] [msg2] + it "max message size, X25519 keys" . twoServers_ proxyCfgV62 proxyCfgV62 $ + deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SX25519 [msg1] [msg2] describe "stress test 1k" $ do let deliver n = deliverMessagesViaProxy srv1 srv2 C.SEd448 [] (map bshow [1 :: Int .. n]) it "1x1000" . twoServersFirstProxy $ deliver 1000 @@ -186,11 +188,14 @@ deliverMessageViaProxy :: (C.AlgorithmI a, C.AuthAlgorithm a) => SMPServer -> SM deliverMessageViaProxy proxyServ relayServ alg msg msg' = deliverMessagesViaProxy proxyServ relayServ alg [msg] [msg'] deliverMessagesViaProxy :: (C.AlgorithmI a, C.AuthAlgorithm a) => SMPServer -> SMPServer -> C.SAlgorithm a -> [ByteString] -> [ByteString] -> IO () -deliverMessagesViaProxy proxyServ relayServ alg unsecuredMsgs securedMsgs = do +deliverMessagesViaProxy = deliverMessagesViaProxyVersion currentClientSMPRelayVersion + +deliverMessagesViaProxyVersion :: (C.AlgorithmI a, C.AuthAlgorithm a) => VersionSMP -> SMPServer -> SMPServer -> C.SAlgorithm a -> [ByteString] -> [ByteString] -> IO () +deliverMessagesViaProxyVersion maxProxyClientVersion proxyServ relayServ alg unsecuredMsgs securedMsgs = do g <- C.newRandom -- set up proxy ts <- getCurrentTime - pc' <- getProtocolClient g (1, proxyServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion} Nothing ts (\_ -> pure ()) + pc' <- getProtocolClient g (1, proxyServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange minServerSMPRelayVersion maxProxyClientVersion} Nothing ts (\_ -> pure ()) pc <- either (fail . show) pure pc' THAuthClient {} <- maybe (fail "getProtocolClient returned no thAuth") pure $ thAuth $ thParams pc -- set up relay From 15513dc662af8cd27bc69e75a1fbfd93075ba34d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Jan 2025 23:15:14 +0000 Subject: [PATCH 12/19] comment, test --- src/Simplex/Messaging/Transport.hs | 13 +++++++++++ tests/SMPProxyTests.hs | 37 ++---------------------------- 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/src/Simplex/Messaging/Transport.hs b/src/Simplex/Messaging/Transport.hs index d537b45c2..ba71b16e6 100644 --- a/src/Simplex/Messaging/Transport.hs +++ b/src/Simplex/Messaging/Transport.hs @@ -604,6 +604,19 @@ smpClientHandshake c ks_ keyHash@(C.KeyHash kh) vRange proxyServer = do let th@THandle {params = THandleParams {sessionId}} = smpTHandle c ServerHandshake {sessionId = sessId, smpVersionRange, authPubKey} <- getHandshake th when (sessionId /= sessId) $ throwE TEBadSession + -- Below logic downgrades version range in case the "client" is SMP proxy server and it is + -- connected to the destination server of the version 11 or older. + -- It disables transport encryption between SMP proxy and destination relay. + -- + -- Prior to version v6.3 the version between proxy and destination was capped at 8, + -- by mistake, which also disables transport encryption and the latest features. + -- + -- Transport encryption between proxy and destination breaks clients with version 10 or earlier, + -- because of a larger message size (see maxMessageLength). + -- + -- To summarize: + -- - proxy and relay version 12: the agreed version is 12, transport encryption disabled (see blockEncryption with proxyServer == True). + -- - proxy is v 12, relay is 11: the agreed version is 10, because of this logic, transport encryption is disabled. let smpVRange = if proxyServer && maxVersion smpVersionRange < blockedEntitySMPVersion then vRange {maxVersion = max (minVersion vRange) deletedEventSMPVersion} diff --git a/tests/SMPProxyTests.hs b/tests/SMPProxyTests.hs index bc5091119..61b7c1670 100644 --- a/tests/SMPProxyTests.hs +++ b/tests/SMPProxyTests.hs @@ -86,36 +86,6 @@ smpProxyTests = do deliverMessageViaProxy proxyServ relayServ C.SEd25519 msg1 msg2 it "max message size, X25519 keys" . twoServersFirstProxy $ deliverMessageViaProxy proxyServ relayServ C.SX25519 msg1 msg2 - xdescribe "client without block encryption, server v6.2 and proxy v6.3 (fails)" $ do - let proxyServ = srv1 - relayServ = srv2 - maxLen' = maxMessageLength deletedEventSMPVersion - (msg1, msg2) <- runIO $ do - g <- C.newRandom - atomically $ (,) <$> C.randomBytes maxLen' g <*> C.randomBytes maxLen' g - it "deliver via proxy" . twoServers_ proxyCfg proxyCfgV62 $ - deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd448 ["hello 1"] ["hello 2"] - it "max message size, Ed448 keys" . twoServers_ proxyCfg proxyCfgV62 $ - deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd448 [msg1] [msg2] - it "max message size, Ed25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ - deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd25519 [msg1] [msg2] - it "max message size, X25519 keys" . twoServers_ proxyCfg proxyCfgV62 $ - deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SX25519 [msg1] [msg2] - xdescribe "client without block encryption, server v6.2 and proxy v6.2 (works)" $ do - let proxyServ = srv1 - relayServ = srv2 - maxLen' = maxMessageLength deletedEventSMPVersion - (msg1, msg2) <- runIO $ do - g <- C.newRandom - atomically $ (,) <$> C.randomBytes maxLen' g <*> C.randomBytes maxLen' g - it "deliver via proxy" . twoServers_ proxyCfgV62 proxyCfgV62 $ - deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd448 ["hello 1"] ["hello 2"] - it "max message size, Ed448 keys" . twoServers_ proxyCfgV62 proxyCfgV62 $ - deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd448 [msg1] [msg2] - it "max message size, Ed25519 keys" . twoServers_ proxyCfgV62 proxyCfgV62 $ - deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SEd25519 [msg1] [msg2] - it "max message size, X25519 keys" . twoServers_ proxyCfgV62 proxyCfgV62 $ - deliverMessagesViaProxyVersion deletedEventSMPVersion proxyServ relayServ C.SX25519 [msg1] [msg2] describe "stress test 1k" $ do let deliver n = deliverMessagesViaProxy srv1 srv2 C.SEd448 [] (map bshow [1 :: Int .. n]) it "1x1000" . twoServersFirstProxy $ deliver 1000 @@ -188,14 +158,11 @@ deliverMessageViaProxy :: (C.AlgorithmI a, C.AuthAlgorithm a) => SMPServer -> SM deliverMessageViaProxy proxyServ relayServ alg msg msg' = deliverMessagesViaProxy proxyServ relayServ alg [msg] [msg'] deliverMessagesViaProxy :: (C.AlgorithmI a, C.AuthAlgorithm a) => SMPServer -> SMPServer -> C.SAlgorithm a -> [ByteString] -> [ByteString] -> IO () -deliverMessagesViaProxy = deliverMessagesViaProxyVersion currentClientSMPRelayVersion - -deliverMessagesViaProxyVersion :: (C.AlgorithmI a, C.AuthAlgorithm a) => VersionSMP -> SMPServer -> SMPServer -> C.SAlgorithm a -> [ByteString] -> [ByteString] -> IO () -deliverMessagesViaProxyVersion maxProxyClientVersion proxyServ relayServ alg unsecuredMsgs securedMsgs = do +deliverMessagesViaProxy proxyServ relayServ alg unsecuredMsgs securedMsgs = do g <- C.newRandom -- set up proxy ts <- getCurrentTime - pc' <- getProtocolClient g (1, proxyServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange minServerSMPRelayVersion maxProxyClientVersion} Nothing ts (\_ -> pure ()) + pc' <- getProtocolClient g (1, proxyServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange minServerSMPRelayVersion currentClientSMPRelayVersion} Nothing ts (\_ -> pure ()) pc <- either (fail . show) pure pc' THAuthClient {} <- maybe (fail "getProtocolClient returned no thAuth") pure $ thAuth $ thParams pc -- set up relay From 61ca96dcbba65a4f2073d9ef0becd999c524af59 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Jan 2025 23:51:59 +0000 Subject: [PATCH 13/19] remove logging tag --- src/Simplex/Messaging/Client.hs | 72 +++++++++---------- src/Simplex/Messaging/Notifications/Client.hs | 2 +- src/Simplex/Messaging/Protocol.hs | 2 +- 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/Simplex/Messaging/Client.hs b/src/Simplex/Messaging/Client.hs index 386e22c23..bde663b32 100644 --- a/src/Simplex/Messaging/Client.hs +++ b/src/Simplex/Messaging/Client.hs @@ -171,7 +171,7 @@ data PClient v err msg = PClient timeoutErrorCount :: TVar Int, clientCorrId :: TVar ChaChaDRG, sentCommands :: TMap CorrId (Request err msg), - sndQ :: TBQueue (Maybe (Request err msg), ByteString, Maybe (Tag (ProtoCommand msg))), + sndQ :: TBQueue (Maybe (Request err msg), ByteString), rcvQ :: TBQueue (NonEmpty (SignedTransmission err msg)), msgQ :: Maybe (TBQueue (ServerTransmissionBatch v err msg)) } @@ -566,14 +566,12 @@ getProtocolClient g transportSession@(_, srv, _) cfg@ProtocolClientConfig {qSize send :: Transport c => ProtocolClient v err msg -> THandle v c 'TClient -> IO () send ProtocolClient {client_ = PClient {sndQ}} h = forever $ atomically (readTBQueue sndQ) >>= sendPending where - sendPending (Nothing, s, tag) = send_ s tag Nothing - sendPending (Just Request {pending, responseVar}, s, tag) = whenM (readTVarIO pending) $ send_ s tag (Just responseVar) - send_ s tag responseVar_ = - tPutLog h s >>= \case - Right () -> pure () - Left e -> do - putStrLn $ "send error: " <> show tag <> ", " <> show e - atomically $ forM_ responseVar_ $ \v -> putTMVar v $ Left $ PCETransportError e + sendPending (r, s) = case r of + Nothing -> void $ tPutLog h s + Just Request {pending, responseVar} -> + whenM (readTVarIO pending) $ tPutLog h s >>= either responseErr pure + where + responseErr = atomically . putTMVar responseVar . Left . PCETransportError receive :: Transport c => ProtocolClient v err msg -> THandle v c 'TClient -> IO () receive ProtocolClient {client_ = PClient {rcvQ, lastReceived, timeoutErrorCount}} h = forever $ do @@ -593,7 +591,7 @@ getProtocolClient g transportSession@(_, srv, _) cfg@ProtocolClientConfig {qSize if remaining > 1_000_000 -- delay pings only for significant time then loop remaining else do - whenM (readTVarIO sendPings) $ void . runExceptT $ sendProtocolCommand c Nothing NoEntity (protocolPing @v @err @msg) Nothing + whenM (readTVarIO sendPings) $ void . runExceptT $ sendProtocolCommand c Nothing NoEntity (protocolPing @v @err @msg) -- sendProtocolCommand/getResponse updates counter for each command cnt <- readTVarIO timeoutErrorCount -- drop client when maxCnt of commands have timed out in sequence, but only after some time has passed after last received response @@ -715,7 +713,7 @@ createSMPQueue :: Bool -> ExceptT SMPClientError IO QueueIdsKeys createSMPQueue c (rKey, rpKey) dhKey auth subMode sndSecure = - sendSMPCommand c (Just rpKey) NoEntity (NEW rKey dhKey auth subMode sndSecure) (Just NEW_) >>= \case + sendSMPCommand c (Just rpKey) NoEntity (NEW rKey dhKey auth subMode sndSecure) >>= \case IDS qik -> pure qik r -> throwE $ unexpectedResponse r @@ -725,7 +723,7 @@ createSMPQueue c (rKey, rpKey) dhKey auth subMode sndSecure = subscribeSMPQueue :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> ExceptT SMPClientError IO () subscribeSMPQueue c rpKey rId = do liftIO $ enablePings c - sendSMPCommand c (Just rpKey) rId SUB (Just SUB_) >>= \case + sendSMPCommand c (Just rpKey) rId SUB >>= \case OK -> pure () cmd@MSG {} -> liftIO $ writeSMPMessage c rId cmd r -> throwE $ unexpectedResponse r @@ -763,7 +761,7 @@ serverTransmission ProtocolClient {thParams = THandleParams {thVersion, sessionI -- https://github.covm/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#receive-a-message-from-the-queue getSMPMessage :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> ExceptT SMPClientError IO (Maybe RcvMessage) getSMPMessage c rpKey rId = - sendSMPCommand c (Just rpKey) rId GET (Just GET_) >>= \case + sendSMPCommand c (Just rpKey) rId GET >>= \case OK -> pure Nothing cmd@(MSG msg) -> liftIO (writeSMPMessage c rId cmd) $> Just msg r -> throwE $ unexpectedResponse r @@ -774,7 +772,7 @@ getSMPMessage c rpKey rId = subscribeSMPQueueNotifications :: SMPClient -> NtfPrivateAuthKey -> NotifierId -> ExceptT SMPClientError IO () subscribeSMPQueueNotifications c npKey nId = do liftIO $ enablePings c - okSMPCommand NSUB (Just NSUB_) c npKey nId + okSMPCommand NSUB c npKey nId {-# INLINE subscribeSMPQueueNotifications #-} -- | Subscribe to multiple SMP queues notifications batching commands if supported. @@ -792,12 +790,12 @@ enablePings ProtocolClient {client_ = PClient {sendPings}} = atomically $ writeT -- -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#secure-queue-command secureSMPQueue :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> SndPublicAuthKey -> ExceptT SMPClientError IO () -secureSMPQueue c rpKey rId senderKey = okSMPCommand (KEY senderKey) (Just KEY_) c rpKey rId +secureSMPQueue c rpKey rId senderKey = okSMPCommand (KEY senderKey) c rpKey rId {-# INLINE secureSMPQueue #-} -- | Secure the SMP queue via sender queue ID. secureSndSMPQueue :: SMPClient -> SndPrivateAuthKey -> SenderId -> SndPublicAuthKey -> ExceptT SMPClientError IO () -secureSndSMPQueue c spKey sId senderKey = okSMPCommand (SKEY senderKey) (Just SKEY_) c spKey sId +secureSndSMPQueue c spKey sId senderKey = okSMPCommand (SKEY senderKey) c spKey sId {-# INLINE secureSndSMPQueue #-} proxySecureSndSMPQueue :: SMPClient -> ProxiedRelay -> SndPrivateAuthKey -> SenderId -> SndPublicAuthKey -> ExceptT SMPClientError IO (Either ProxyClientError ()) @@ -809,7 +807,7 @@ proxySecureSndSMPQueue c proxiedRelay spKey sId senderKey = proxySMPCommand c pr -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#enable-notifications-command enableSMPQueueNotifications :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> NtfPublicAuthKey -> RcvNtfPublicDhKey -> ExceptT SMPClientError IO (NotifierId, RcvNtfPublicDhKey) enableSMPQueueNotifications c rpKey rId notifierKey rcvNtfPublicDhKey = - sendSMPCommand c (Just rpKey) rId (NKEY notifierKey rcvNtfPublicDhKey) (Just NKEY_) >>= \case + sendSMPCommand c (Just rpKey) rId (NKEY notifierKey rcvNtfPublicDhKey) >>= \case NID nId rcvNtfSrvPublicDhKey -> pure (nId, rcvNtfSrvPublicDhKey) r -> throwE $ unexpectedResponse r @@ -827,7 +825,7 @@ enableSMPQueuesNtfs c qs = L.map process <$> sendProtocolCommands c cs -- -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#disable-notifications-command disableSMPQueueNotifications :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> ExceptT SMPClientError IO () -disableSMPQueueNotifications = okSMPCommand NDEL (Just NDEL_) +disableSMPQueueNotifications = okSMPCommand NDEL {-# INLINE disableSMPQueueNotifications #-} -- | Disable notifications for multiple queues for push notifications server. @@ -840,7 +838,7 @@ disableSMPQueuesNtfs = okSMPCommands NDEL -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#send-message sendSMPMessage :: SMPClient -> Maybe SndPrivateAuthKey -> SenderId -> MsgFlags -> MsgBody -> ExceptT SMPClientError IO () sendSMPMessage c spKey sId flags msg = - sendSMPCommand c spKey sId (SEND flags msg) (Just SEND_) >>= \case + sendSMPCommand c spKey sId (SEND flags msg) >>= \case OK -> pure () r -> throwE $ unexpectedResponse r @@ -852,7 +850,7 @@ proxySMPMessage c proxiedRelay spKey sId flags msg = proxySMPCommand c proxiedRe -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#acknowledge-message-delivery ackSMPMessage :: SMPClient -> RcvPrivateAuthKey -> QueueId -> MsgId -> ExceptT SMPClientError IO () ackSMPMessage c rpKey rId msgId = - sendSMPCommand c (Just rpKey) rId (ACK msgId) (Just ACK_) >>= \case + sendSMPCommand c (Just rpKey) rId (ACK msgId) >>= \case OK -> return () cmd@MSG {} -> liftIO $ writeSMPMessage c rId cmd r -> throwE $ unexpectedResponse r @@ -862,14 +860,14 @@ ackSMPMessage c rpKey rId msgId = -- -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#suspend-queue suspendSMPQueue :: SMPClient -> RcvPrivateAuthKey -> QueueId -> ExceptT SMPClientError IO () -suspendSMPQueue = okSMPCommand OFF (Just OFF_) +suspendSMPQueue = okSMPCommand OFF {-# INLINE suspendSMPQueue #-} -- | Irreversibly delete SMP queue and all messages in it. -- -- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#delete-queue deleteSMPQueue :: SMPClient -> RcvPrivateAuthKey -> RecipientId -> ExceptT SMPClientError IO () -deleteSMPQueue = okSMPCommand DEL (Just DEL_) +deleteSMPQueue = okSMPCommand DEL {-# INLINE deleteSMPQueue #-} -- | Delete multiple SMP queues batching commands if supported. @@ -882,7 +880,7 @@ deleteSMPQueues = okSMPCommands DEL connectSMPProxiedRelay :: SMPClient -> SMPServer -> Maybe BasicAuth -> ExceptT SMPClientError IO ProxiedRelay connectSMPProxiedRelay c@ProtocolClient {client_ = PClient {tcpConnectTimeout, tcpTimeout}} relayServ@ProtocolServer {keyHash = C.KeyHash kh} proxyAuth | thVersion (thParams c) >= sendingProxySMPVersion = - sendProtocolCommand_ c Nothing tOut Nothing NoEntity (Cmd SProxiedClient (PRXY relayServ proxyAuth)) (Just $ CT SProxiedClient PRXY_) >>= \case + sendProtocolCommand_ c Nothing tOut Nothing NoEntity (Cmd SProxiedClient (PRXY relayServ proxyAuth)) >>= \case PKEY sId vr (chain, key) -> case supportedClientSMPRelayVRange `compatibleVersion` vr of Nothing -> throwE $ transportErr TEVersion @@ -984,7 +982,7 @@ proxySMPCommand c@ProtocolClient {thParams = proxyThParams, client_ = PClient {c et <- liftEitherWith PCECryptoError $ EncTransmission <$> C.cbEncrypt cmdSecret nonce b paddedProxiedTLength -- proxy interaction errors are wrapped let tOut = Just $ 2 * tcpTimeout - tryE (sendProtocolCommand_ c (Just nonce) tOut Nothing (EntityId sessionId) (Cmd SProxiedClient (PFWD v cmdPubKey et)) (Just $ CT SProxiedClient PFWD_)) >>= \case + tryE (sendProtocolCommand_ c (Just nonce) tOut Nothing (EntityId sessionId) (Cmd SProxiedClient (PFWD v cmdPubKey et))) >>= \case Right r -> case r of PRES (EncResponse er) -> do -- server interaction errors are thrown directly @@ -1020,7 +1018,7 @@ forwardSMPTransmission c@ProtocolClient {thParams, client_ = PClient {clientCorr let fwdT = FwdTransmission {fwdCorrId, fwdVersion, fwdKey, fwdTransmission} eft = EncFwdTransmission $ C.cbEncryptNoPad sessSecret nonce (smpEncode fwdT) -- send - sendProtocolCommand_ c (Just nonce) Nothing Nothing NoEntity (Cmd SSender (RFWD eft)) (Just $ CT SSender RFWD_) >>= \case + sendProtocolCommand_ c (Just nonce) Nothing Nothing NoEntity (Cmd SSender (RFWD eft)) >>= \case RRES (EncFwdResponse efr) -> do -- unwrap r' <- liftEitherWith PCECryptoError $ C.cbDecryptNoPad sessSecret (C.reverseNonce nonce) efr @@ -1030,13 +1028,13 @@ forwardSMPTransmission c@ProtocolClient {thParams, client_ = PClient {clientCorr getSMPQueueInfo :: SMPClient -> C.APrivateAuthKey -> QueueId -> ExceptT SMPClientError IO QueueInfo getSMPQueueInfo c pKey qId = - sendSMPCommand c (Just pKey) qId QUE (Just QUE_) >>= \case + sendSMPCommand c (Just pKey) qId QUE >>= \case INFO info -> pure info r -> throwE $ unexpectedResponse r -okSMPCommand :: PartyI p => Command p -> Maybe (CommandTag p) -> SMPClient -> C.APrivateAuthKey -> QueueId -> ExceptT SMPClientError IO () -okSMPCommand cmd cmdTag c pKey qId = - sendSMPCommand c (Just pKey) qId cmd cmdTag >>= \case +okSMPCommand :: PartyI p => Command p -> SMPClient -> C.APrivateAuthKey -> QueueId -> ExceptT SMPClientError IO () +okSMPCommand cmd c pKey qId = + sendSMPCommand c (Just pKey) qId cmd >>= \case OK -> return () r -> throwE $ unexpectedResponse r @@ -1051,8 +1049,8 @@ okSMPCommands cmd c qs = L.map process <$> sendProtocolCommands c cs Left e -> Left e -- | Send SMP command -sendSMPCommand :: PartyI p => SMPClient -> Maybe C.APrivateAuthKey -> QueueId -> Command p -> Maybe (CommandTag p) -> ExceptT SMPClientError IO BrokerMsg -sendSMPCommand c pKey qId cmd cmdTag = sendProtocolCommand c pKey qId (Cmd sParty cmd) (CT sParty <$> cmdTag) +sendSMPCommand :: PartyI p => SMPClient -> Maybe C.APrivateAuthKey -> QueueId -> Command p -> ExceptT SMPClientError IO BrokerMsg +sendSMPCommand c pKey qId cmd = sendProtocolCommand c pKey qId (Cmd sParty cmd) {-# INLINE sendSMPCommand #-} type PCTransmission err msg = (Either TransportError SentRawTransmission, Request err msg) @@ -1088,23 +1086,23 @@ sendBatch c@ProtocolClient {client_ = PClient {sndQ}} b = do pure [Response entityId $ Left $ PCETransportError e] TBTransmissions s n rs | n > 0 -> do - atomically $ writeTBQueue sndQ (Nothing, s, Nothing) -- do not expire batched responses + atomically $ writeTBQueue sndQ (Nothing, s) -- do not expire batched responses mapConcurrently (getResponse c Nothing) rs | otherwise -> pure [] TBTransmission s r -> do - atomically $ writeTBQueue sndQ (Nothing, s, Nothing) + atomically $ writeTBQueue sndQ (Nothing, s) (: []) <$> getResponse c Nothing r -- | Send Protocol command -sendProtocolCommand :: forall v err msg. Protocol v err msg => ProtocolClient v err msg -> Maybe C.APrivateAuthKey -> EntityId -> ProtoCommand msg -> Maybe (Tag (ProtoCommand msg)) -> ExceptT (ProtocolClientError err) IO msg +sendProtocolCommand :: forall v err msg. Protocol v err msg => ProtocolClient v err msg -> Maybe C.APrivateAuthKey -> EntityId -> ProtoCommand msg -> ExceptT (ProtocolClientError err) IO msg sendProtocolCommand c = sendProtocolCommand_ c Nothing Nothing -- Currently there is coupling - batch commands do not expire, and individually sent commands do. -- This is to reflect the fact that we send subscriptions only as batches, and also because we do not track a separate timeout for the whole batch, so it is not obvious when should we expire it. -- We could expire a batch of deletes, for example, either when the first response expires or when the last one does. -- But a better solution is to process delayed delete responses. -sendProtocolCommand_ :: forall v err msg. Protocol v err msg => ProtocolClient v err msg -> Maybe C.CbNonce -> Maybe Int -> Maybe C.APrivateAuthKey -> EntityId -> ProtoCommand msg -> Maybe (Tag (ProtoCommand msg)) -> ExceptT (ProtocolClientError err) IO msg -sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THandleParams {batch, blockSize}} nonce_ tOut pKey entId cmd cmdTag = +sendProtocolCommand_ :: forall v err msg. Protocol v err msg => ProtocolClient v err msg -> Maybe C.CbNonce -> Maybe Int -> Maybe C.APrivateAuthKey -> EntityId -> ProtoCommand msg -> ExceptT (ProtocolClientError err) IO msg +sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THandleParams {batch, blockSize}} nonce_ tOut pKey entId cmd = ExceptT $ uncurry sendRecv =<< mkTransmission_ c nonce_ (pKey, entId, cmd) where -- two separate "atomically" needed to avoid blocking @@ -1114,7 +1112,7 @@ sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THan Right t | B.length s > blockSize - 2 -> pure . Left $ PCETransportError TELargeMsg | otherwise -> do - atomically $ writeTBQueue sndQ (Just r, s, cmdTag) + atomically $ writeTBQueue sndQ (Just r, s) response <$> getResponse c tOut r where s diff --git a/src/Simplex/Messaging/Notifications/Client.hs b/src/Simplex/Messaging/Notifications/Client.hs index af80474a2..273010c2c 100644 --- a/src/Simplex/Messaging/Notifications/Client.hs +++ b/src/Simplex/Messaging/Notifications/Client.hs @@ -87,7 +87,7 @@ ntfDeleteSubscription = okNtfCommand SDEL -- | Send notification server command sendNtfCommand :: NtfEntityI e => NtfClient -> Maybe C.APrivateAuthKey -> NtfEntityId -> NtfCommand e -> ExceptT NtfClientError IO NtfResponse -sendNtfCommand c pKey entId cmd = sendProtocolCommand c pKey entId (NtfCmd sNtfEntity cmd) Nothing +sendNtfCommand c pKey entId cmd = sendProtocolCommand c pKey entId (NtfCmd sNtfEntity cmd) okNtfCommand :: NtfEntityI e => NtfCommand e -> NtfClient -> C.APrivateAuthKey -> NtfEntityId -> ExceptT NtfClientError IO () okNtfCommand cmd c pKey entId = diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index 2d54629b4..43e032d0b 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -1359,7 +1359,7 @@ instance Protocol SMPVersion ErrorType BrokerMsg where ERR e -> Just e _ -> Nothing -class (ProtocolMsgTag (Tag msg), Show (Tag msg)) => ProtocolEncoding v err msg | msg -> err, msg -> v where +class ProtocolMsgTag (Tag msg) => ProtocolEncoding v err msg | msg -> err, msg -> v where type Tag msg encodeProtocol :: Version v -> msg -> ByteString protocolP :: Version v -> Tag msg -> Parser msg From fe898ac9c95acdd4a95ade898c6e6d69ff9cdc83 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Jan 2025 23:55:41 +0000 Subject: [PATCH 14/19] remove logs --- src/Simplex/FileTransfer/Protocol.hs | 2 -- .../Messaging/Notifications/Protocol.hs | 2 -- src/Simplex/Messaging/Protocol.hs | 19 ++----------------- src/Simplex/Messaging/Server.hs | 19 ------------------- tests/SMPClient.hs | 8 -------- 5 files changed, 2 insertions(+), 48 deletions(-) diff --git a/src/Simplex/FileTransfer/Protocol.hs b/src/Simplex/FileTransfer/Protocol.hs index ac736ed94..9bf552732 100644 --- a/src/Simplex/FileTransfer/Protocol.hs +++ b/src/Simplex/FileTransfer/Protocol.hs @@ -111,8 +111,6 @@ deriving instance Show (FileCommandTag p) data FileCmdTag = forall p. FilePartyI p => FCT (SFileParty p) (FileCommandTag p) -deriving instance Show FileCmdTag - instance FilePartyI p => Encoding (FileCommandTag p) where smpEncode = \case FNEW_ -> "FNEW" diff --git a/src/Simplex/Messaging/Notifications/Protocol.hs b/src/Simplex/Messaging/Notifications/Protocol.hs index 914f4df3c..96f8b337e 100644 --- a/src/Simplex/Messaging/Notifications/Protocol.hs +++ b/src/Simplex/Messaging/Notifications/Protocol.hs @@ -80,8 +80,6 @@ deriving instance Show (NtfCommandTag e) data NtfCmdTag = forall e. NtfEntityI e => NCT (SNtfEntity e) (NtfCommandTag e) -deriving instance Show NtfCmdTag - instance NtfEntityI e => Encoding (NtfCommandTag e) where smpEncode = \case TNEW_ -> "TNEW" diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index 43e032d0b..679f077b7 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -60,7 +60,6 @@ module Simplex.Messaging.Protocol SenderCanSecure, Party (..), Cmd (..), - CmdTag (..), DirectParty, BrokerMsg (..), SParty (..), @@ -1695,26 +1694,12 @@ tPut th@THandle {params} = fmap concat . mapM tPutBatch . batchTransmissions (ba TBTransmission s _ -> (: []) <$> tPutLog th s tPutLog :: Transport c => THandle v c p -> ByteString -> IO (Either TransportError ()) -tPutLog th@THandle {params} s = do +tPutLog th s = do r <- tPutBlock th s case r of - Left e -> putStrLn ("tPut error: " <> show e <> paramsStr) + Left e -> putStrLn ("tPut error: " <> show e) _ -> pure () pure r - where - paramsStr = - ", block size " - <> show (B.length s) - <> ", thServerVRange = " - <> show (thServerVRange params) - <> ", thVersion = " - <> show (thVersion params) - <> ", thAuth = " - <> show (isJust $ thAuth params) - <> ", implySessId = " - <> show (implySessId params) - <> ", encryptBlock = " - <> show (isJust $ encryptBlock params) -- ByteString in TBTransmissions includes byte with transmissions count data TransportBatch r = TBTransmissions ByteString Int [r] | TBTransmission ByteString r | TBError TransportError r diff --git a/src/Simplex/Messaging/Server.hs b/src/Simplex/Messaging/Server.hs index 13c140cd4..d31a50e34 100644 --- a/src/Simplex/Messaging/Server.hs +++ b/src/Simplex/Messaging/Server.hs @@ -1217,25 +1217,6 @@ client Right r -> PRES r <$ inc own pSuccesses Left e -> ERR (smpProxyError e) <$ case e of PCEProtocolError {} -> inc own pSuccesses - PCETransportError e' -> do - liftIO $ putStrLn $ "forwardSMPTransmission error: " <> show e' <> paramsStr - inc own pErrorsOther - where - THandleParams {thVersion = v'} = thParams' - EncTransmission s = encBlock - paramsStr = - ", block size " - <> show (B.length s) - <> ", thServerVRange = " - <> show (thServerVRange thParams') - <> ", thVersion = " - <> show v' - <> ", thAuth = " - <> show (isJust $ thAuth thParams') - <> ", implySessId = " - <> show (implySessId thParams') - <> ", encryptBlock = " - <> show (isJust $ encryptBlock thParams') _ -> inc own pErrorsOther else Just (ERR $ transportErr TEVersion) <$ inc own pErrorsCompat where diff --git a/tests/SMPClient.hs b/tests/SMPClient.hs index 53d7a2fdc..5ce0eb7f6 100644 --- a/tests/SMPClient.hs +++ b/tests/SMPClient.hs @@ -190,14 +190,6 @@ proxyCfg = where smpAgentCfg' = smpAgentCfg cfg -proxyCfgV62 :: ServerConfig -proxyCfgV62 = - proxyCfg - { smpAgentCfg = smpAgentCfg' {smpCfg = (smpCfg smpAgentCfg') {serverVRange = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion}} - } - where - smpAgentCfg' = smpAgentCfg proxyCfg - proxyVRangeV8 :: VersionRangeSMP proxyVRangeV8 = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion From a81166f2c5b241fb568f067eb6790710d30b2565 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 19 Jan 2025 00:20:24 +0000 Subject: [PATCH 15/19] version --- simplexmq.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplexmq.cabal b/simplexmq.cabal index 9c8953375..081f29c9f 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -1,7 +1,7 @@ cabal-version: 1.12 name: simplexmq -version: 6.3.0.103 +version: 6.3.0.104 synopsis: SimpleXMQ message broker description: This package includes <./docs/Simplex-Messaging-Server.html server>, <./docs/Simplex-Messaging-Client.html client> and From 55c8ba798f6b8c8e85baf578b2e2b09c69dd1e2b Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 19 Jan 2025 16:49:07 +0000 Subject: [PATCH 16/19] SMP version 14 --- src/Simplex/Messaging/Transport.hs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Simplex/Messaging/Transport.hs b/src/Simplex/Messaging/Transport.hs index ba71b16e6..ce5e4bac7 100644 --- a/src/Simplex/Messaging/Transport.hs +++ b/src/Simplex/Messaging/Transport.hs @@ -145,7 +145,8 @@ smpBlockSize = 16384 -- 9 - faster handshake: SKEY command for sender to secure queue (6/30/2024) -- 10 - DELD event to subscriber when queue is deleted via another connnection (9/11/2024) -- 11 - additional encryption of transport blocks with forward secrecy (10/06/2024) --- 12 - BLOCKED error for blocked queues, handshake property to disable transport encryption between server and proxy (1/11/2025) +-- 12 - BLOCKED error for blocked queues (1/11/2025) +-- 14 - proxyServer handshake property to disable transport encryption between server and proxy (1/19/2025) data SMPVersion @@ -179,6 +180,9 @@ encryptedBlockSMPVersion = VersionSMP 11 blockedEntitySMPVersion :: VersionSMP blockedEntitySMPVersion = VersionSMP 12 +proxyServerHandshakeSMPVersion :: VersionSMP +proxyServerHandshakeSMPVersion = VersionSMP 14 + minClientSMPRelayVersion :: VersionSMP minClientSMPRelayVersion = VersionSMP 6 @@ -186,13 +190,13 @@ minServerSMPRelayVersion :: VersionSMP minServerSMPRelayVersion = VersionSMP 6 currentClientSMPRelayVersion :: VersionSMP -currentClientSMPRelayVersion = VersionSMP 12 +currentClientSMPRelayVersion = VersionSMP 14 legacyServerSMPRelayVersion :: VersionSMP legacyServerSMPRelayVersion = VersionSMP 6 currentServerSMPRelayVersion :: VersionSMP -currentServerSMPRelayVersion = VersionSMP 12 +currentServerSMPRelayVersion = VersionSMP 14 -- Max SMP protocol version to be used in e2e encrypted -- connection between client and server, as defined by SMP proxy. @@ -200,7 +204,7 @@ currentServerSMPRelayVersion = VersionSMP 12 -- to prevent client version fingerprinting by the -- destination relays when clients upgrade at different times. proxiedSMPRelayVersion :: VersionSMP -proxiedSMPRelayVersion = VersionSMP 12 +proxiedSMPRelayVersion = VersionSMP 14 -- minimal supported protocol version is 6 -- TODO remove code that supports sending commands without batching @@ -480,7 +484,7 @@ instance Encoding ClientHandshake where pure ClientHandshake {smpVersion = v, keyHash, authPubKey, proxyServer} ifHasProxy :: VersionSMP -> a -> a -> a -ifHasProxy v a b = if v >= blockedEntitySMPVersion then a else b +ifHasProxy v a b = if v >= proxyServerHandshakeSMPVersion then a else b instance Encoding ServerHandshake where smpEncode ServerHandshake {smpVersionRange, sessionId, authPubKey} = @@ -618,7 +622,7 @@ smpClientHandshake c ks_ keyHash@(C.KeyHash kh) vRange proxyServer = do -- - proxy and relay version 12: the agreed version is 12, transport encryption disabled (see blockEncryption with proxyServer == True). -- - proxy is v 12, relay is 11: the agreed version is 10, because of this logic, transport encryption is disabled. let smpVRange = - if proxyServer && maxVersion smpVersionRange < blockedEntitySMPVersion + if proxyServer && maxVersion smpVersionRange < proxyServerHandshakeSMPVersion then vRange {maxVersion = max (minVersion vRange) deletedEventSMPVersion} else vRange case smpVersionRange `compatibleVRange` smpVRange of From e9c78a94e44e2931edc84cb01476db3e6b5a0e94 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 19 Jan 2025 17:38:38 +0000 Subject: [PATCH 17/19] version --- simplexmq.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplexmq.cabal b/simplexmq.cabal index 081f29c9f..6ac9fae7e 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -1,7 +1,7 @@ cabal-version: 1.12 name: simplexmq -version: 6.3.0.104 +version: 6.3.0.105 synopsis: SimpleXMQ message broker description: This package includes <./docs/Simplex-Messaging-Server.html server>, <./docs/Simplex-Messaging-Client.html client> and From 0a6cef205907d71163adaf251d11a9d55eeeacfc Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 20 Jan 2025 08:33:43 +0000 Subject: [PATCH 18/19] remove comments --- src/Simplex/Messaging/Transport.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Simplex/Messaging/Transport.hs b/src/Simplex/Messaging/Transport.hs index ce5e4bac7..67cb83d01 100644 --- a/src/Simplex/Messaging/Transport.hs +++ b/src/Simplex/Messaging/Transport.hs @@ -217,11 +217,9 @@ legacyServerSMPRelayVRange = mkVersionRange minServerSMPRelayVersion legacyServe supportedServerSMPRelayVRange :: VersionRangeSMP supportedServerSMPRelayVRange = mkVersionRange minServerSMPRelayVersion currentServerSMPRelayVersion --- cap it temporarily to version 10 supportedProxyClientSMPRelayVRange :: VersionRangeSMP supportedProxyClientSMPRelayVRange = mkVersionRange minServerSMPRelayVersion currentServerSMPRelayVersion --- This range initially allows only version 8 - see the comment above. proxiedSMPRelayVRange :: VersionRangeSMP proxiedSMPRelayVRange = mkVersionRange sendingProxySMPVersion proxiedSMPRelayVersion From 0c7a8ab5b34e93c0c548a4ccf7a4173a8d3e7a72 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 20 Jan 2025 13:03:29 +0000 Subject: [PATCH 19/19] version --- simplexmq.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplexmq.cabal b/simplexmq.cabal index 6ac9fae7e..460d8deb5 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -1,7 +1,7 @@ cabal-version: 1.12 name: simplexmq -version: 6.3.0.105 +version: 6.3.0.1 synopsis: SimpleXMQ message broker description: This package includes <./docs/Simplex-Messaging-Server.html server>, <./docs/Simplex-Messaging-Client.html client> and