From 0ac5b1808d181c1b5f5bac012b4bcfe30b86d971 Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Tue, 29 Sep 2026 19:52:51 +0000 Subject: [PATCH] consider IP address protected only if it is used for the chosen host --- src/Simplex/Messaging/Agent/Client.hs | 5 +++-- src/Simplex/Messaging/Client.hs | 19 ++++++++++++------- tests/CoreTests/SOCKSSettings.hs | 22 +++++++++++++++++++++- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Simplex/Messaging/Agent/Client.hs b/src/Simplex/Messaging/Agent/Client.hs index 8cbb4c17b..867568579 100644 --- a/src/Simplex/Messaging/Agent/Client.hs +++ b/src/Simplex/Messaging/Agent/Client.hs @@ -1261,9 +1261,10 @@ sendOrProxySMPCommand c nm userId destSrv@ProtocolServer {host = destHosts} conn Left e -> throwE e ipAddressProtected :: NetworkConfig -> ProtocolServer p -> Bool -ipAddressProtected NetworkConfig {socksProxy, hostMode} (ProtocolServer _ hosts _ _) = do - isJust socksProxy || (hostMode == HMOnion && any isOnionHost hosts) +ipAddressProtected cfg@NetworkConfig {socksProxy, hostMode} (ProtocolServer _ hosts _ _) = + either (const $ isJust socksProxy) protected $ chooseTransportHost cfg hosts where + protected h = isJust (useSocksProxy cfg h) || (hostMode == HMOnion && isOnionHost h) isOnionHost = \case THOnionHost _ -> True; _ -> False withNtfClient :: AgentClient -> NetworkRequestMode -> NtfServer -> EntityId -> ByteString -> (NtfClient -> ExceptT NtfClientError IO a) -> AM a diff --git a/src/Simplex/Messaging/Client.hs b/src/Simplex/Messaging/Client.hs index f8f1a4cb9..d61877273 100644 --- a/src/Simplex/Messaging/Client.hs +++ b/src/Simplex/Messaging/Client.hs @@ -102,6 +102,7 @@ module Simplex.Messaging.Client defaultSMPClientConfig, defaultNetworkConfig, transportClientConfig, + useSocksProxy, clientSocksCredentials, chooseTransportHost, temporaryClientError, @@ -170,7 +171,7 @@ import Simplex.Messaging.SimplexName (SimplexDomain, fullDomainName) import Simplex.Messaging.TMap (TMap) import qualified Simplex.Messaging.TMap as TM import Simplex.Messaging.Transport -import Simplex.Messaging.Transport.Client (SocksAuth (..), SocksProxyWithAuth (..), TransportClientConfig (..), TransportHost (..), defaultSMPPort, runTransportClient) +import Simplex.Messaging.Transport.Client (SocksAuth (..), SocksProxy, SocksProxyWithAuth (..), TransportClientConfig (..), TransportHost (..), defaultSMPPort, runTransportClient) import Simplex.Messaging.Transport.HTTP2 (httpALPN11) import Simplex.Messaging.Transport.KeepAlive import Simplex.Messaging.Transport.Shared (ChainCertificates (..), chainIdCaCerts, x509validate) @@ -439,15 +440,19 @@ defaultNetworkConfig = } transportClientConfig :: NetworkConfig -> NetworkRequestMode -> TransportHost -> Bool -> Maybe [ALPN] -> TransportClientConfig -transportClientConfig NetworkConfig {socksProxy, socksMode, tcpConnectTimeout, tcpKeepAlive, logTLSErrors} nm host useSNI clientALPN = - TransportClientConfig {socksProxy = useSocksProxy socksMode, tcpConnectTimeout = tOut, tcpKeepAlive, logTLSErrors, clientCredentials = Nothing, clientALPN, useSNI} +transportClientConfig cfg@NetworkConfig {tcpConnectTimeout, tcpKeepAlive, logTLSErrors} nm host useSNI clientALPN = + TransportClientConfig {socksProxy = useSocksProxy cfg host, tcpConnectTimeout = tOut, tcpKeepAlive, logTLSErrors, clientCredentials = Nothing, clientALPN, useSNI} where tOut = netTimeoutInt tcpConnectTimeout nm + +useSocksProxy :: NetworkConfig -> TransportHost -> Maybe SocksProxy +useSocksProxy NetworkConfig {socksProxy, socksMode} host = case socksMode of + SMAlways -> socksProxy' + SMOnion -> case host of + THOnionHost _ -> socksProxy' + _ -> Nothing + where socksProxy' = (\(SocksProxyWithAuth _ proxy) -> proxy) <$> socksProxy - useSocksProxy SMAlways = socksProxy' - useSocksProxy SMOnion = case host of - THOnionHost _ -> socksProxy' - _ -> Nothing clientSocksCredentials :: ProtocolTypeI (ProtoType msg) => NetworkConfig -> UTCTime -> TransportSession msg -> Maybe SocksCredentials clientSocksCredentials NetworkConfig {socksProxy, sessionMode} proxySessTs (userId, srv, entityId_) = case socksProxy of diff --git a/tests/CoreTests/SOCKSSettings.hs b/tests/CoreTests/SOCKSSettings.hs index 3dbf5e5e7..cf447a50e 100644 --- a/tests/CoreTests/SOCKSSettings.hs +++ b/tests/CoreTests/SOCKSSettings.hs @@ -2,15 +2,18 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-ambiguous-fields #-} module CoreTests.SOCKSSettings where import Network.Socket (SockAddr (..), tupleToHostAddress) +import Simplex.Messaging.Agent.Client (ipAddressProtected) import Simplex.Messaging.Client +import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding.String -import Simplex.Messaging.Protocol (ErrorType) +import Simplex.Messaging.Protocol (ErrorType, pattern SMPServer) import Simplex.Messaging.Transport.Client import Test.Hspec hiding (fit, it) import Util @@ -19,6 +22,7 @@ socksSettingsTests :: Spec socksSettingsTests = do describe "hostMode and requiredHostMode settings" testHostMode describe "socksMode setting, independent of hostMode setting" testSocksMode + describe "ipAddressProtected, consistent with chosen host and socksMode" testIPAddressProtected describe "socks proxy address encoding" testSocksProxyEncoding testPublicHost :: TransportHost @@ -94,6 +98,22 @@ testSocksMode = do let TransportClientConfig {socksProxy} = transportClientConfig cfg NRMInteractive host False Nothing in socksProxy +testIPAddressProtected :: Spec +testIPAddressProtected = do + it "should be protected if SOCKS proxy is used for the chosen host" $ do + protected SMAlways HMOnionViaSocks [testPublicHost] `shouldBe` True + protected SMOnion HMOnionViaSocks [testPublicHost, testOnionHost] `shouldBe` True + protected SMOnion HMPublic [testOnionHost] `shouldBe` True + it "should not be protected if SOCKS proxy is not used for the chosen host" $ do + protected SMOnion HMOnionViaSocks [testPublicHost] `shouldBe` False + protected SMOnion HMPublic [testPublicHost, testOnionHost] `shouldBe` False + it "should be protected if SOCKS proxy is specified and required host is not available" $ do + protectedCfg defaultNetworkConfig {requiredHostMode = True} SMOnion HMOnionViaSocks [testPublicHost] `shouldBe` True + where + protected = protectedCfg defaultNetworkConfig + protectedCfg cfg socksMode hostMode hosts = + ipAddressProtected cfg {socksProxy = Just defaultSocksProxyWithAuth, socksMode, hostMode} (SMPServer hosts "" (C.KeyHash "")) + testSocksProxyEncoding :: Spec testSocksProxyEncoding = do it "should decode SOCKS proxy with isolate-by-auth mode" $ do