mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-02 22:11:54 +00:00
core: align resolver docs/tests with RSLV errors
This commit is contained in:
@@ -4633,11 +4633,12 @@ processChatCommand cxt nm = \case
|
||||
|
||||
-- | Failure modes for 'resolveOnUserServers' / 'iterateResolvers'.
|
||||
data ResolveError
|
||||
= -- | No enabled server can resolve: every candidate answered CMD UNKNOWN /
|
||||
-- PROHIBITED (does not speak RSLV), or none were configured / reachable.
|
||||
-- Iterating across unsupported servers is privacy-safe -- the client degrades
|
||||
-- RSLV to a no-op for relays below namesSMPVersion (see Protocol.hs), so an
|
||||
-- unsupported relay never receives the queried name.
|
||||
= -- | No enabled server can resolve: every candidate answered CMD UNKNOWN
|
||||
-- (predates RSLV) or CMD PROHIBITED (speaks RSLV but has no resolver
|
||||
-- configured), or none were configured / reachable. Iterating across these is
|
||||
-- safe: a CMD UNKNOWN relay never received the name (the client degrades RSLV
|
||||
-- to a no-op below namesSMPVersion, see Protocol.hs); a CMD PROHIBITED relay
|
||||
-- did receive it but is one the user already trusts as an SMP server.
|
||||
ResolverUnavailable
|
||||
| -- | AUTH from a name-capable server. Every name server reads the same on-chain state, so we trust the first one's no.
|
||||
NameNotRegistered
|
||||
@@ -4664,13 +4665,15 @@ enabledSMPServersForUser user = do
|
||||
| otherwise = Nothing
|
||||
|
||||
-- | Resolve a SimpleX name by trying the user's enabled SMP servers in order.
|
||||
-- Transport-level failures (NETWORK, TIMEOUT, host-unreachable) and unsupported
|
||||
-- servers (CMD UNKNOWN / PROHIBITED, i.e. relays that don't speak RSLV) both fall
|
||||
-- through to the next server. Skipping an unsupported relay discloses nothing:
|
||||
-- the client degrades RSLV to a no-op below namesSMPVersion, so the name is never
|
||||
-- sent to it. A definitive answer from a name-capable relay terminates iteration:
|
||||
-- AUTH is definitive NotFound (every name server reads the same on-chain state);
|
||||
-- any other definite error surfaces as ResolverTransport.
|
||||
-- Transport-level failures (NETWORK, TIMEOUT, host-unreachable) and servers that
|
||||
-- cannot resolve (CMD UNKNOWN -- predates RSLV; or CMD PROHIBITED -- speaks RSLV
|
||||
-- but has no resolver configured) all fall through to the next server. A CMD
|
||||
-- UNKNOWN relay never received the name (the client degrades RSLV below
|
||||
-- namesSMPVersion); a CMD PROHIBITED relay did, but it is one the user already
|
||||
-- trusts as an SMP server. A definitive answer from a name-capable relay
|
||||
-- terminates iteration: AUTH is definitive NotFound (every name server reads the
|
||||
-- same on-chain state); any other definite error (e.g. INTERNAL on a resolver
|
||||
-- backend failure) surfaces as ResolverTransport.
|
||||
-- Privacy: a name-capable relay does see the queried name, so once one has
|
||||
-- answered we do not broadcast the miss to every other operator the user has.
|
||||
resolveOnUserServers :: User -> SimplexNameDomain -> CM (Either ResolveError NameRecord)
|
||||
@@ -4858,12 +4861,14 @@ iterateResolvers servers resolve = go servers
|
||||
isNotRegistered = \case
|
||||
SMP _ SMP.AUTH -> True
|
||||
_ -> False
|
||||
-- A server that doesn't speak RSLV answers CMD UNKNOWN (predates the
|
||||
-- command, e.g. the official servers) or CMD PROHIBITED (knows it but gates
|
||||
-- on namesSMPVersion) -- directly, or wrapped by a proxy. We skip it and try
|
||||
-- the next server: the client degrades RSLV to a no-op below namesSMPVersion
|
||||
-- (Protocol.hs), so an unsupported relay never received the queried name.
|
||||
-- ResolverUnavailable is returned only when no server can resolve.
|
||||
-- A server that cannot resolve answers CMD UNKNOWN -- it predates RSLV (e.g.
|
||||
-- an old official server), and the client degraded RSLV to a no-op below
|
||||
-- namesSMPVersion so it never received the name -- or CMD PROHIBITED -- it
|
||||
-- speaks RSLV but has no resolver configured (names role off), so it did
|
||||
-- receive the name but cannot help. Either form may arrive directly or wrapped
|
||||
-- by a proxy. We skip it and try the next server; ResolverUnavailable is
|
||||
-- returned only when no server can resolve. A resolver-backed server's
|
||||
-- transient failure is INTERNAL (-> ResolverTransport), not handled here.
|
||||
isUnsupported = \case
|
||||
SMP _ (SMP.CMD SMP.UNKNOWN) -> True
|
||||
SMP _ (SMP.CMD SMP.PROHIBITED) -> True
|
||||
|
||||
@@ -62,6 +62,15 @@ resolveNameTests = do
|
||||
-- second server must NOT be consulted: definite error means the server
|
||||
-- answered, so iterating would leak the queried name.
|
||||
readIORef callsRef `shouldReturn` [srv1]
|
||||
it "surfaces a resolver-backend INTERNAL as ResolverTransport, not NotFound" $ do
|
||||
callsRef <- newIORef []
|
||||
r <- iterateResolvers [srv1, srv2] (recording callsRef (\_ -> pure $ Left backendErr))
|
||||
case r of
|
||||
Left (ResolverTransport e) -> e `shouldBe` backendErr
|
||||
other -> expectationFailure $ "expected ResolverTransport, got " <> show other
|
||||
-- a backend failure on srv1 stops iteration (the name reached a capable
|
||||
-- relay); it must not be reported as the authoritative NameNotRegistered.
|
||||
readIORef callsRef `shouldReturn` [srv1]
|
||||
it "iterates on transport-level errors and uses the next server's success" $ do
|
||||
callsRef <- newIORef []
|
||||
r <- iterateResolvers [srv1, srv2] (recording callsRef stubTransportThenHit)
|
||||
@@ -230,7 +239,9 @@ sampleRecord =
|
||||
authErr :: AgentErrorType
|
||||
authErr = SMP "smp1.example" SMP.AUTH
|
||||
|
||||
-- CMD PROHIBITED on the PFWD path: surfaces as PROXY ... (ProxyProtocolError ...).
|
||||
-- A relay with no resolver configured (names role off) answers CMD PROHIBITED
|
||||
-- (Server.hs). A relay error is transparent over the proxy (SMP host ...); the
|
||||
-- PROXY-wrapped form here exercises a proxy-level rejection. Both -> skip.
|
||||
prohibitedErr :: AgentErrorType
|
||||
prohibitedErr = PROXY "proxy.example" "smp1.example" (ProxyProtocolError (SMP.CMD SMP.PROHIBITED))
|
||||
|
||||
@@ -245,3 +256,10 @@ networkErr = BROKER "smp1.example" (NETWORK (NEConnectError "simulated network f
|
||||
-- as ResolverTransport without iterating, to avoid broadcasting the name.
|
||||
otherDefiniteErr :: AgentErrorType
|
||||
otherDefiniteErr = INTERNAL "simulated definite error"
|
||||
|
||||
-- A resolver-backed relay whose backing store failed (resolver 5xx, timeout,
|
||||
-- decode error) answers ERR INTERNAL (Server.hs), surfacing as SMP host INTERNAL.
|
||||
-- This is transient -- it must surface as ResolverTransport, NOT collapse to the
|
||||
-- authoritative NameNotRegistered the way the old ERR-AUTH-for-everything did.
|
||||
backendErr :: AgentErrorType
|
||||
backendErr = SMP "smp1.example" SMP.INTERNAL
|
||||
|
||||
Reference in New Issue
Block a user