java.lang.Object
net.i2p.router.transport.udp.EstablishmentManager

public class EstablishmentManagerextends Object
Coordinate the establishment of new sessions - both inbound and outbound. This has its own thread to add packets to the packet queue when necessary, as well as to drop any failed establishment attempts.
  • Field Details

    • _context

      private final RouterContext _context
      The router context.
    • _log

      private final Log _log
      Logger for SSU connection establishment.
    • _transport

      private final UDPTransport _transport
      The SSU transport we serve.
    • _networkID

      private final int _networkID
      The network id we communicate on.
    • _banLogger

      private final BanLogger _banLogger
      Records why routers were banned.
    • _builder2

      private final PacketBuilder2 _builder2
      Builder for SSU2 packets.
    • _outboundTokens

      private final Map<RemoteHostId, EstablishmentManager.Token> _outboundTokens
      Tokens we hold for hosts we contact.
    • _inboundTokens

      private final Map<RemoteHostId, EstablishmentManager.Token> _inboundTokens
      Tokens issued to hosts that contacted us.
    • _terminationCounter

      private final ObjectCounter<RemoteHostId> _terminationCounter
      Counts termination messages per host.
    • _inboundTokenLifetimeStat

      private volatile RateStat _inboundTokenLifetimeStat
      Cached "udp.inboundTokenLifetime" RateStat handle. Registered in the constructor and never replaced, so it is resolved once rather than on every getInboundToken(RemoteHostId) call: that path is reachable from an unauthenticated inbound Token Request, so the stat lookup (a name-based map get followed by a synchronized read of the Rate) was attacker triggerable. Resolved lazily because a test may construct this manager before the stat exists.
    • _inboundStates

      private final ConcurrentHashMap<RemoteHostId, InboundEstablishState> _inboundStates
      Map of RemoteHostId to InboundEstablishState.
    • _outboundStates

      private final ConcurrentHashMap<RemoteHostId, OutboundEstablishState> _outboundStates
      Map of RemoteHostId to OutboundEstablishState. The key could be either an IP/Port (for direct) or a Hash (for indirect, before the RelayResponse is received). Once the RelayResponse is received we change the key.
    • _queuedOutbound

      Map of RemoteHostId to a bounded, thread-safe queue of OutNetMessage instances for peers whose outbound establishment is pending due to concurrency limits. Each queue is capped at MAX_QUEUED_PER_PEER messages. Thread-safe: no external synchronization required.
    • _liveIntroductions

      private final ConcurrentHashMap<Long, OutboundEstablishState> _liveIntroductions
      Map of nonce (Long) to OutboundEstablishState. Only for indirect, before we receive the RelayResponse. This is so we can lookup state for the RelayResponse. After we receive the relay response, _outboundStates is keyed by actual IP.
    • _outboundByClaimedAddress

      private final ConcurrentHashMap<RemoteHostId, OutboundEstablishState> _outboundByClaimedAddress
      Map of claimed IP/port to OutboundEstablishState. Only for indirect, before we receive the RelayResponse. This is so we can lookup a pending introduction by IP even before we know the "real" IP, so we can match an inbound packet. After we receive the relay response, _outboundStates is keyed by actual IP.
    • _outboundByHash

      private final ConcurrentHashMap<Hash, OutboundEstablishState> _outboundByHash
      Map of router hash to OutboundEstablishState. Only for indirect, after we receive the RelayResponse. This is so we can lookup a pending connection by Hash even after we've got the IP/port, so we can match a subsequent outbound packet. Before we receive the relay response, _outboundStates is keyed by hash.
    • _inboundBans

      private final Map<RemoteHostId, Long> _inboundBans
      Temporary inbound bans after previous IB failure, to prevent excessive DH. SSU 2. Value is expiration time.
    • _alive

      private volatile boolean _alive
      False once the manager has shut down.
    • _activityLock

      private final Object _activityLock
      Lock for the establishment activity count.
    • _activity

      private int _activity
      Establishment attempts currently in flight.
    • DEFAULT_MAX_CONCURRENT_ESTABLISH

      private final int DEFAULT_MAX_CONCURRENT_ESTABLISH
      Max outbound in progress - max inbound is half of this.
    • _defaultLowMaxConcurrentEstablish

      private static volatile int _defaultLowMaxConcurrentEstablish
      Default concurrent establishments for a low profile.
    • _defaultHighMaxConcurrentEstablish

      private static volatile int _defaultHighMaxConcurrentEstablish
      Default concurrent establishments for a high profile.
    • PROP_MAX_CONCURRENT_ESTABLISH

      private static final String PROP_MAX_CONCURRENT_ESTABLISH
      Config key for the concurrent establishment limit.
      See Also:
    • _tunedMaxConcurrentEstablish

      private static volatile int _tunedMaxConcurrentEstablish
      Tuned max concurrent establish, -1 = use config. Set by Tuner.
    • MAX_QUEUED_OUTBOUND

      private static volatile int MAX_QUEUED_OUTBOUND
      Max pending outbound connections (waiting because we are at MAX_CONCURRENT_ESTABLISH).
    • MAX_QUEUED_PER_PEER

      private static final int MAX_QUEUED_PER_PEER
      Max queued msgs per peer while peer connection is queued.
      See Also:
    • BAN_DURATION_INVALID_ADDRESS_MS

      private static final long BAN_DURATION_INVALID_ADDRESS_MS
      Ban duration for a peer that supplied an invalid or spoofed SSU address, an unusable MTU, or a bad introduction key.
      See Also:
    • MAX_NONCE

      private static final long MAX_NONCE
      Largest possible handshake nonce value.
      See Also:
    • MAX_OB_ESTABLISH_TIME

      static final AtomicLong MAX_OB_ESTABLISH_TIME
      Kill any outbound that takes more than this. Two round trips (Req-Created-Confirmed-Data) for direct; 3 1/2 round trips (RReq-RResp+Intro-HolePunch-Req-Created-Confirmed-Data) for indirect. Tuned to ~3-5x average establish time. The Tuner further adjusts this at runtime.
    • MAX_IB_ESTABLISH_TIME

      static final AtomicLong MAX_IB_ESTABLISH_TIME
      Kill any inbound that takes more than this. One round trip (Created-Confirmed), possibly two for SSU2 with retry. Tuned to ~3-5x average establish time; the Tuner further adjusts this at runtime.
    • IB_RETRY_SENT_MAX_TIME

      private static final long IB_RETRY_SENT_MAX_TIME
      Inbound retry-sent limit: how long we wait to get a session request back after a retry, before giving up (5x the retransmit delay).
    • OB_RETRY_EXTRA_MS

      private static final long OB_RETRY_EXTRA_MS
      Extra establishment budget granted to an outbound attempt once a resend has been scheduled and still failed.

      Additive rather than an absolute like the inbound IB_RETRY_SENT_MAX_TIME. The inbound figure is 5x its retransmit delay, but the outbound delay is 300ms against the inbound 1000ms, so 5x would be 1500ms - already below the base deadline and therefore incapable of ever binding. An additive extension expresses the same intent at either delay: give a peer that missed our first messages more room, without extending the attempts that never got a reply to begin with.

      See Also:
    • CORRUPT_CONFIRM_LOG_INTERVAL_MS

      static final long CORRUPT_CONFIRM_LOG_INTERVAL_MS
      Minimum gap between "Corrupt SessionConfirmed" reports.

      This one warning was 9.2% of an entire log - roughly 180 lines a minute - and it carries no reason, so the volume bought no information. Two causes are folded into this one catch: a peer that legitimately failed the handshake and then sent SessionConfirmed anyway, which throws IllegalStateException on an already-FAILED state and is ordinary race behaviour; and a genuine key or payload rejection. They need opposite responses, so the reason is now reported and repeats are counted.

      See Also:
    • INBOUND_GIVEUP_LOG_INTERVAL_MS

      static final long INBOUND_GIVEUP_LOG_INTERVAL_MS
      Minimum gap between inbound establishment give-up reports. @since 0.9.71+.
      See Also:
    • _lastInboundGiveupLog

      private static final AtomicLong _lastInboundGiveupLog
      When the inbound giveup was last logged.
    • _inboundGiveupSuppressed

      private static final AtomicLong _inboundGiveupSuppressed
      Giveup logs suppressed since the last one.
    • _lastCorruptConfirmLog

      private static final AtomicLong _lastCorruptConfirmLog
      When a corrupt confirm was last logged.
    • _corruptConfirmSuppressed

      private static final AtomicLong _corruptConfirmSuppressed
      Corrupt confirm logs suppressed since the last one.
    • SLOW_ESTABLISH_MS

      static final long SLOW_ESTABLISH_MS
      Completion time above which a handshake counts as slow.

      The recorded udp.*EstablishTime stats only expose a mean, and the mean is useless for sizing the deadline: measured live, successful outbound handshakes average 196ms, so 4x the mean targets 784ms - below the 2250 that abandoned 117 handshakes a minute. Latency here is heavily right-skewed, and the abandonments came from the tail, not the body. This threshold marks the tail so a tuner has something to read. It sits a quarter of the validated floor, so a healthy link records none.

      See Also:
    • OB_MESSAGE_TIMEOUT

      public static final long OB_MESSAGE_TIMEOUT
      Max wait before receiving a response to a single message during outbound establishment.
      See Also:
    • DATA_MESSAGE_TIMEOUT

      static final AtomicLong DATA_MESSAGE_TIMEOUT
      For the DSM and or netdb store.
    • IB_BAN_TIME

      private static final long IB_BAN_TIME
      How long an inbound establishment failure bans a peer.
      See Also:
    • MIN_TOKENS

      private static final int MIN_TOKENS
      Fewest tokens issued to one host.
    • MAX_TOKENS

      private static final int MAX_TOKENS
      Most tokens issued to one host.
    • IB_TOKEN_EXPIRATION

      public static final long IB_TOKEN_EXPIRATION
      How long an inbound token stays valid, in milliseconds.
      See Also:
    • MAX_SKEW

      private static final long MAX_SKEW
      Maximum clock skew accepted in a handshake.
      See Also:
    • TOKEN_FILE

      private static final String TOKEN_FILE
      File holding the SSU2 tokens across restarts.
      See Also:
    • MAX_TERMINATIONS

      private static final int MAX_TERMINATIONS
      Max immediate terminations to send to a peer every FAILSAFE_INTERVAL.
      See Also:
    • MAX_TAG_VALUE

      public static final long MAX_TAG_VALUE
      The relay tag is a 4-byte field in the protocol.
      See Also:
  • Constructor Details

    • EstablishmentManager

      public EstablishmentManager(RouterContext ctx, UDPTransport transport)
      Creates the establishment manager.
      Parameters:
      ctx - the router context
      transport - the transport we establish sessions for
  • Method Details

    • getDefaultLowMaxConcurrentEstablish

      public static int getDefaultLowMaxConcurrentEstablish()
      The default low max concurrent establish.
      Returns:
      the default cap on concurrent outbound sessions before scaling
    • setDefaultLowMaxConcurrentEstablish

      public static void setDefaultLowMaxConcurrentEstablish(int val)
      The default low max concurrent establish, bounded 32-4096.
      Parameters:
      val - the desired max concurrent outbound sessions, clamped to 32-4096
    • getDefaultHighMaxConcurrentEstablish

      public static int getDefaultHighMaxConcurrentEstablish()
      The default high max concurrent establish.
      Returns:
      the default ceiling on concurrent outbound sessions
    • setDefaultHighMaxConcurrentEstablish

      public static void setDefaultHighMaxConcurrentEstablish(int val)
      The default high max concurrent establish, bounded 64-8192.
      Parameters:
      val - the desired max concurrent outbound sessions, clamped to 64-8192
    • setMaxConcurrentEstablish

      public static void setMaxConcurrentEstablish(int val)
      Set the max concurrent establish (called by Tuner).
      Parameters:
      val - the max concurrent outbound sessions
    • getMaxConcurrentEstablishTuned

      public static int getMaxConcurrentEstablishTuned(RouterContext ctx)
      The max concurrent establish in effect, tuned value if set else config.
      Parameters:
      ctx - the router context
      Returns:
      the max concurrent outbound sessions
    • getMaxQueuedOutbound

      public static int getMaxQueuedOutbound()
      The current max queued outbound connections.
      Returns:
      the limit currently in effect
    • setMaxQueuedOutbound

      public static void setMaxQueuedOutbound(int max)
      The max queued outbound connections (called by Tuner).
      Parameters:
      max - the maximum number of queued connections
    • getMaxIbEstablishTime

      static long getMaxIbEstablishTime()
      The inbound establishment base deadline, as the Tuner has it set.

      Exposed so the inbound state machine can schedule its next send from the same budget the expiry check uses. When those two were derived independently they drifted, and a state could become due for a retransmission while still short of its deadline - which the establisher then spun through, retransmitting and logging at WARN on every pass.

      Returns:
      the base deadline in milliseconds
    • getIbRetrySentMaxTime

      static long getIbRetrySentMaxTime()
      The extended budget an inbound attempt gets once it has sent a retry.
      Returns:
      the retry budget in milliseconds
    • inboundEstablishBudget

      static long inboundEstablishBudget(boolean isRetrySent, long maxEstablishTime, long retrySentMaxTime)
      The deadline an inbound attempt was actually given.

      This was written as a disjunction - lifetime > base || (retried && lifetime >= retryBudget) - which never extended anything. The first clause expires the attempt at the base on its own, so the second could only matter when the retry budget was the smaller of the two, and then it made a retried attempt die sooner. Observed live: with a 4000ms base and a 3750ms retry budget, a retried attempt died at 3751ms.

      The predicate now asks this method for the deadline, so the two cannot disagree and the retry budget means what IB_RETRY_SENT_MAX_TIME exists for: five retransmit delays of extra patience for an attempt that has already tried again.

      Parameters:
      isRetrySent - true if the attempt had already sent a retry
      maxEstablishTime - the base deadline
      retrySentMaxTime - the extended budget available after a retry
      Returns:
      the effective deadline in milliseconds
    • shouldLogInboundGiveup

      static boolean shouldLogInboundGiveup(long now)
      Whether to emit an inbound give-up report now, counting any skipped.
      Parameters:
      now - current time in milliseconds
      Returns:
      true if the caller should log; false if the event was only counted
    • getInboundGiveupSuppressed

      static long getInboundGiveupSuppressed()
      Inbound give-up events rate-limited away since the last report.
      Returns:
      the number of suppressed give-up events
    • shouldLogCorruptConfirm

      static boolean shouldLogCorruptConfirm(long now)
      Whether to emit a corrupt-SessionConfirmed report now, counting any skipped.
      Parameters:
      now - current wall clock
      Returns:
      true if this caller should log; false if the event was only counted
    • getCorruptConfirmSuppressed

      static long getCorruptConfirmSuppressed()
      How many corrupt-SessionConfirmed events were rate-limited away.
      Returns:
      suppressed event count since the last emitted report
    • corruptConfirmReason

      static String corruptConfirmReason(Throwable e)
      The reason a SessionConfirmed was rejected, for the log line. The RuntimeException and GeneralSecurityException wrappers just re-throw the real handshake failure, so report the root cause.
      Parameters:
      e - the exception thrown out of the handshake
      Returns:
      a short, single-line description including the exception type
    • startup

      public void startup()
      Start the establishment manager.
    • shutdown

      public void shutdown()
      Shut down the establishment manager.
    • getInboundState

      InboundEstablishState getInboundState(RemoteHostId from)
      Grab the active establishing state.
      Parameters:
      from - the peer's IP and port, keying the inbound state map
      Returns:
      null if none
    • getOutboundState

      OutboundEstablishState getOutboundState(RemoteHostId from)
      Grab the active establishing state.
      Parameters:
      from - the peer's IP and port, tried against the claimed and hash-keyed maps
      Returns:
      null if none
    • getMaxConcurrentEstablish

      private int getMaxConcurrentEstablish()
      How many concurrent outbound sessions to deal with.
      Returns:
      the max concurrent establish
    • isBanlisted

      static boolean isBanlisted(Banlist banlist, Hash h)
      Is the router with the given hash banlisted under any active ban? Null-safe. Shared by the establish, receiveSessionOrTokenRequest, and handleInbound confirmed-complete paths. Any active ban - temporary, hostile, or permanent - blocks inbound SSU2 connections: a banlisted peer is one the local router does not talk to at all (inbound or outbound) until the ban expires. Banlist.isBanlisted(Hash) returns true for every non-expired entry, so it covers all tiers.
      Parameters:
      banlist - the router's banlist
      h - the peer hash, may be null
      Returns:
      true if the peer has an active ban of any tier
    • isBlocklisted

      static boolean isBlocklisted(Blocklist blocklist, byte[] ip)
      Is the given IP address in the router's blocklist? Null-safe. Shared by the session/establish and hole-punch paths.
      Parameters:
      blocklist - the router's blocklist
      ip - the IP address, may be null
      Returns:
      true if the IP is blocklisted
    • hasUsefulMessage

      static boolean hasUsefulMessage(Throwable t)
      Does the exception carry a message worth showing to the user? Legacy code stores the string "null" in the message.
      Parameters:
      t - may be null
      Returns:
      true if a useful message is present
    • canBeSigner

      static boolean canBeSigner(OutboundEstablishState2.IntroState istate)
      Can the given introducer have signed the relay response? A peer that was never contacted, terminated the session, or explicitly rejected the hole punch cannot be the signer.
      Parameters:
      istate - the intro state
      Returns:
      true if the introducer may be the signer
    • isWrongNetwork

      static boolean isWrongNetwork(int id, int ourNetworkId)
      Is the peer's network ID one we will not establish to? The router communicates only with peers in its own network.
      Parameters:
      id - the peer's network ID
      ourNetworkId - this router's network ID
      Returns:
      true if the network IDs differ
    • isUnspecifiedNetwork

      static boolean isUnspecifiedNetwork(int id)
      Is the peer's network ID unset (i.e. the legacy -1)? Used to pick the ban reason and duration when isWrongNetwork(int, int) rejects the peer: unset networks get a shorter temporary ban, while a present-but-different network ID is banned indefinitely.
      Parameters:
      id - the peer's network ID
      Returns:
      true if the ID is the "no network specified" sentinel
    • isUsableUDPAddress

      static boolean isUsableUDPAddress(InetAddress remAddr, int port)
      Is the claimed address usable for a direct connection? A null address or a port outside 1-65535 is useless to us.
      Parameters:
      remAddr - the claimed host address, may be null
      port - the claimed port
      Returns:
      true if the address can be used directly
    • isInvalidPeerIP

      static boolean isInvalidPeerIP(boolean isValid, boolean equalsExternalIP, boolean allowLocal)
      Is the claimed IP address unusable, either because it isn't a routable public address or because it is ours without local connections allowed? The caller supplies the per-transport validity and "equals external IP" booleans so this stays a pure decision.
      Parameters:
      isValid - the transport's verdict on the address
      equalsExternalIP - true if the address equals the router's external IP
      allowLocal - true if local connections are permitted
      Returns:
      true if the address must be rejected
    • needsIndirect

      static boolean needsIndirect(boolean hasIntroducers, boolean noDirectAddress)
      Should this establish use the peer's introducers rather than the claimed address? True when the peer offers introducers or when no usable direct address was found.
      Parameters:
      hasIntroducers - true if the address advertises introducers
      noDirectAddress - true if no claimed address was usable
      Returns:
      true to establish indirectly
    • shouldQueueOutbound

      static boolean shouldQueueOutbound(boolean queueIfMaxExceeded, int activeCount, int maxConcurrent)
      Should the message be put on the deferred queue instead of starting a new outbound state? Only when queueing is requested and the active outbound establish count is at or above the configured cap.
      Parameters:
      queueIfMaxExceeded - true normally, false when called from locked_admit
      activeCount - the current number of outbound establish states
      maxConcurrent - the configured cap
      Returns:
      true to queue the message
    • shouldRejectQueue

      static boolean shouldRejectQueue(boolean peerAlreadyQueued, int queuedOutboundSize, int maxQueued)
      Should the queued message be rejected outright rather than queued? True when the global deferred queue is at capacity and this peer does not already have a queue we could append to.
      Parameters:
      peerAlreadyQueued - true if this peer already has a deferred queue
      queuedOutboundSize - the global deferred queue count
      maxQueued - the global queue cap
      Returns:
      true to reject the message
    • queueAtCapacity

      static boolean queueAtCapacity(int queueCount, int maxPerPeer)
      Has this peer's deferred queue hit its per-peer cap?
      Parameters:
      queueCount - the number of messages now queued for the peer
      maxPerPeer - the per-peer queue cap
      Returns:
      true if the queue is at capacity
    • nextIntroStateforPeerCheck

      static OutboundEstablishState2.IntroState nextIntroStateforPeerCheck(OutboundEstablishState2.IntroState istate, boolean peerPresent, boolean peerSupported)
      Advance an introducer's state based on whether we already hold a live PeerState for it. Handles the connected-peer walk in handlePendingIntro(OutboundEstablishState): an INIT/CONNECTING intro that resolves to a live peer becomes CONNECTED when the peer speaks a supported version, otherwise REJECTED (cross-version relaying is not implemented yet), and a CONNECTED intro whose peer has gone away becomes DISCONNECTED.
      Parameters:
      istate - the current intro state
      peerPresent - true if a live PeerState was found
      peerSupported - true if the peer advertises a supported SSU2 version
      Returns:
      the advanced intro state (unchanged for states not handled here)
    • nextIntroStateforLocalLookup

      static OutboundEstablishState2.IntroState nextIntroStateforLocalLookup(OutboundEstablishState2.IntroState istate, boolean riFound)
      Advance an introducer's state based on a local RouterInfo lookup. Only intros we are allowed to connect to (INIT, LOOKUP_SENT, HAS_RI) are advanced, and only to HAS_RI when a local RouterInfo is known; every other state is returned unchanged.
      Parameters:
      istate - the current intro state
      riFound - true if a RouterInfo was found locally
      Returns:
      the advanced intro state (unchanged unless a local RI is found)
    • isUsableUDPAddress

      static boolean isUsableUDPAddress(byte[] ip, int port)
      Is a raw IP/port pair usable to try a direct connection to an introducer? Identity with isUsableUDPAddress(InetAddress, int) except operating on the already-extracted address bytes.
      Parameters:
      ip - the address bytes, may be null
      port - the claimed UDP port, valid 1-65535
      Returns:
      true if the address can be used
    • isTerminalOutboundState

      static boolean isTerminalOutboundState(OutboundEstablishState.OutboundState state)
      Is the outbound establish state in a terminal state that should be dropped and processed (completed or failed) rather than retried? Guards the scan loop in handleOutbound().
      Parameters:
      state - the outbound state
      Returns:
      true for CONFIRMED_COMPLETELY or VALIDATION_FAILED
    • hasObMessageTimedOut

      static boolean hasObMessageTimedOut(long lastSentTime, long timeoutMs, long now)
      Has the outbound state gone unanswered for at least the message timeout? A zero/unknown send time never counts as timed out. Shared by the REQUEST_SENT / CONFIRMED_PARTIALLY / PENDING_INTRO dispatch arms in handleOutbound().
      Parameters:
      lastSentTime - the time we last sent, 0 if never
      timeoutMs - the per-message timeout
      now - current time in milliseconds
      Returns:
      true if a message was sent and has been unanswered too long
    • shouldFailObState

      static boolean shouldFailObState(boolean expired, long lastSentTime, long timeoutMs, long now)
      Should the outbound establish state be processed as expired, either because its lifetime is over or because it has been unanswered for the message timeout?
      Parameters:
      expired - true if the state's lifetime is exhausted
      lastSentTime - the time we last sent, 0 if never
      timeoutMs - the per-message timeout
      now - current time in milliseconds
      Returns:
      true to process the state as expired
    • isSendDue

      static boolean isSendDue(long nextSendTime, long now)
      Is the state due to send now (its next send time has arrived)?
      Parameters:
      nextSendTime - the scheduled send time
      now - current time in milliseconds
      Returns:
      true if the send is due
    • hasOutboundEstablishExpired

      static boolean hasOutboundEstablishExpired(long lifetime, boolean hasRetried, long maxEstablishTime, long retryExtraMs)
      Whether an outbound establishment attempt has run out of time.

      Mirrors hasInboundEstablishExpired(long, boolean, long, long) for the outbound direction, which until now had no equivalent: the outbound deadline was a flat lifetime >= MAX_OB_ESTABLISH_TIME with no retry grace, while the inbound side got up to IB_RETRY_SENT_MAX_TIME once a retry was sent. That made the direction which retransmits three times faster (300ms against 1000ms) the one held to the shorter patience, and it is the direction the SSU2 token exchange - one round trip more than SSU1 - runs through.

      Parameters:
      lifetime - how long the attempt has been alive, in milliseconds
      hasRetried - true if a resend has already been scheduled
      maxEstablishTime - the base deadline, normally MAX_OB_ESTABLISH_TIME
      retryExtraMs - additional budget granted once a resend has failed
      Returns:
      true if the attempt should be abandoned
    • hasInboundEstablishExpired

      static boolean hasInboundEstablishExpired(long lifetime, boolean isRetrySent, long maxEstablishTime, long retrySentMaxTime)
      Has the inbound establish state lived long enough to be expired, either past the overall establish cap or, when we are waiting for a session request after sending a retry, past the retry-sent limit?
      Parameters:
      lifetime - how long the state has existed, in milliseconds
      isRetrySent - true if the state is waiting in IB_STATE_RETRY_SENT
      maxEstablishTime - the overall inbound establish cap
      retrySentMaxTime - the extra cap applied only to retry-sent states
      Returns:
      true if the inbound state should be expired
    • isBobRelayReject

      static boolean isBobRelayReject(int code)
      Is this a rejection from the introducer (Bob) rather than from Charlie or a success? SSU2 relay response codes 1-63 reject at the introducer layer. 0 is success and >= 64 reject at the destination (Charlie). Both the live-introduction map handling and the signer selection in receiveRelayResponse(PeerState2, long, int, byte[]) branch on this.
      Parameters:
      code - the relay response code
      Returns:
      true if the introducer rejected the request
    • relayResponseIntroState

      static OutboundEstablishState2.IntroState relayResponseIntroState(int code)
      The intro state to record for a relay response, by response code: 0 means Charlie accepted, codes 1-63 mean Bob (the introducer) rejected, and codes 64+ mean Charlie rejected.
      Parameters:
      code - the relay response code
      Returns:
      the matching intro state; State selection must mirror isBobRelayReject(int)
    • isBadRelayDataAddress

      static boolean isBadRelayDataAddress(boolean validPort, boolean validIP, boolean tooClose, boolean sameAsIntroducer, boolean blocklisted)
      Is the claimed relay address unacceptable, meaning Charlie asked us to connect somewhere invalid, too close, on our own network, or a blocklisted address? Finding the introducer's own IP in the claim is also rejection-worthy (a relay must not send us back to itself).
      Parameters:
      validPort - whether the port is in range
      validIP - whether the transport accepts the address
      tooClose - whether the address is too close to ours
      sameAsIntroducer - whether the address is Bob's own IP
      blocklisted - whether the address is in the blocklist
      Returns:
      true if any check fails
    • hasValidV2Introducer

      static boolean hasValidV2Introducer(UDPAddress addr, long now)
      Does the SSU2 address advertise at least one usable v2 introducer, meaning a hash that hasn't expired (expiration 0 means never expires)? Used by createOutboundState(OutNetMessage, RemoteHostId, RemoteHostId, RouterIdentity, boolean, UDPAddress, RouterAddress, Hash, String, String, int, long, boolean) before building a state for an indirect peer, since without any current introducer the establishment can never succeed.
      Parameters:
      addr - the peer's address
      now - the current time
      Returns:
      true if any introducer hash is present and unexpired
    • isMtuTooSmall

      static boolean isMtuTooSmall(int mtu, int ourMTU)
      Is either our MTU or the peer's advertised MTU below the minimum usable for SSU2? MTUs of 0 are treated as unknown and pass, matching the original inline check.
      Parameters:
      mtu - the peer's advertised MTU
      ourMTU - our MTU for the same address family
      Returns:
      true if either is positive and below PeerState2.MIN_MTU
    • establish

      public void establish(OutNetMessage msg)
      Send the message to its specified recipient by establishing a connection with them and sending it off. This call does not block, and on failure, the message is failed.

      Note - if we go back to multiple PacketHandler threads, this may need more locking.

      Parameters:
      msg - the outbound message to send, queued if we are at the concurrency limit
    • establish

      private void establish(OutNetMessage msg, boolean queueIfMaxExceeded)
      Establishes a connection to send the message, or queues it if the limit is exceeded.
      Parameters:
      msg - the outbound message to send, queued if we are at the concurrency limit
      queueIfMaxExceeded - true normally, false if called from locked_admit so we don't loop
    • queueToInboundState

      private void queueToInboundState(InboundEstablishState inState, OutNetMessage msg)
      Queue the message on an inbound establish state that's already in progress, or send/fail it immediately if the state is already complete or failed.
      Parameters:
      inState - non-null
      msg - the outbound message to queue on the state, or send or fail right away if the state is already finished
    • lookupOutboundState

      private OutboundEstablishState lookupOutboundState(RemoteHostId to, Hash toHash)
      Look up an existing outbound establish state for the peer, first by address, then by hash (for indirect connections).
      Parameters:
      to - the claimed address or hash key the state is filed under
      toHash - the peer's router hash, the fallback key tried when the address key misses
      Returns:
      the state, or null if none
    • createOutboundState

      private OutboundEstablishState createOutboundState(OutNetMessage msg, RemoteHostId to, RemoteHostId maybeTo, RouterIdentity toIdentity, boolean isIndirect, UDPAddress addr, RouterAddress ra, Hash toHash, String truncHash, String ipAddress, int maybePort, long now, boolean isBanned)
      Create a new outbound establish state for the peer, after verifying that its introducers, MTU, and introduction key are usable. On failure, marks the peer unreachable and fails the message, banning the peer if it isn't already banned.
      Parameters:
      msg - the outbound message to fail if the peer turns out to be unusable
      to - the key in _outboundStates: the claimed address (direct) or the hash (indirect)
      maybeTo - the claimed address, or null if indirect
      toIdentity - the peer's router identity, stored in the new state
      isIndirect - true to reach the peer through an introducer, so a valid v2 introducer is required
      addr - the peer's parsed SSU address options, source of the MTU and the introducer list
      ra - the peer's router address entry, source of the SSU version, transport style and introduction key option
      toHash - the peer's router hash, used for bans and unreachable marking
      truncHash - the first 6 base64 chars of toHash, for logging only
      ipAddress - the claimed IP as text, empty when there is no claimed address
      maybePort - the claimed port, 0 when there is no claimed address
      now - the current time in ms, used for the introducer validity check
      isBanned - true if the peer is already banlisted, so no new ban is added
      Returns:
      the new state, or null if the message was failed
    • banAndFail

      private void banAndFail(OutNetMessage msg, Hash toHash, String failReason, String banReason, String noHashWarn, String ipAddress, int port, String truncHash, long banDuration, long now, boolean isBanned)
      Mark the peer unreachable and fail the message. If the peer is known (has a hash) and not already banned, ban it for the given duration and log a warning; otherwise warn that the claimed address is bad.
      Parameters:
      msg - the outbound message to fail
      toHash - the peer's router hash, or null when the peer cannot be named, in which case no ban is recorded
      failReason - reason given to the failed message
      banReason - reason used for the ban and the ban log
      noHashWarn - warning text used when the peer has no hash
      ipAddress - the claimed IP, may be empty for indirect peers
      port - the claimed port
      truncHash - the first 6 base64 chars of toHash, for the warning log
      banDuration - ban duration in ms, e.g. BAN_DURATION_INVALID_ADDRESS_MS
      now - the current time in ms, the base for the ban's expiry
      isBanned - true if the peer is already banlisted, so no new ban is added
    • getMaxInboundEstablishers

      private int getMaxInboundEstablishers()
      How many concurrent inbound sessions to deal with.
      Returns:
      the max inbound establishers
    • shouldAllowInboundEstablishment

      public boolean shouldAllowInboundEstablishment()
      Should we allow another inbound establishment?
      Returns:
      whether allow inbound establishment
    • receiveSessionOrTokenRequest

      void receiveSessionOrTokenRequest(RemoteHostId from, InboundEstablishState2 state, UDPPacket packet)
      Got a SessionRequest OR a TokenRequest (initiates an inbound establishment)

      SSU 2 only.

      Parameters:
      from - the sender's IP and port, not yet a trusted peer identity
      state - as looked up in PacketHandler, but null unless retransmitted or retry sent
      packet - header decrypted only
    • sendTerminationPacket

      private void sendTerminationPacket(RemoteHostId to, UDPPacket fromPacket, int terminationCode)
      Send a Retry packet with a termination code, for a rejection of a session/token request. No InboundEstablishState2 required.

      SSU 2 only. The inbound packet was superficially validated for type, netID, and version, so we have basic probing resistance. The Retry packet encryption is low-cost, chacha only.

      Rate limited to MAX_TERMINATIONS per peer every FAILSAFE_INTERVAL.

      Parameters:
      to - the peer's address to reject, ignored if null or banlisted
      fromPacket - header already decrypted, must be session or token request
      terminationCode - SSU2 reason code placed in the Retry packet
    • receiveSessionConfirmed

      void receiveSessionConfirmed(InboundEstablishState2 state, UDPPacket packet)
      Got a SessionConfirmed, should only happen as part of an inbound establishment.

      SSU 2 only.

      Parameters:
      state - non-null
      packet - header decrypted only
    • receiveSessionCreated

      void receiveSessionCreated(OutboundEstablishState2 state, UDPPacket packet)
      Got a SessionCreated (in response to our outbound SessionRequest)

      SSU 2 only.

      Parameters:
      state - non-null
      packet - header decrypted only
    • receiveRetry

      void receiveRetry(OutboundEstablishState2 state, UDPPacket packet)
      Got a Retry (in response to our outbound SessionRequest or TokenRequest)

      SSU 2 only.

      Parameters:
      state - the outbound attempt that our Retry went to, null if we sent none
      packet - header decrypted only, the Retry itself is parsed by the state
    • receiveSessionDestroy

      void receiveSessionDestroy(RemoteHostId from, PeerState state)
      Got a SessionDestroy on an established conn

      SSU 2.

      Parameters:
      from - the sender, logged only - the authenticated state decides what to drop
      state - the established session to tear down
    • receiveSessionDestroy

      void receiveSessionDestroy(RemoteHostId from, OutboundEstablishState state)
      Got a SessionDestroy during outbound establish

      SSU 2.

      Parameters:
      from - the sender, which keys the outbound state to abandon
      state - the outbound attempt being torn down
    • receiveSessionDestroy

      void receiveSessionDestroy(RemoteHostId from)
      Got a SessionDestroy - maybe during an inbound establish?

      A SessionDestroy received before a session exists is only informational: there is no state to drop, and a peer that never completed an establishment has nothing to tear down. The inbound establish is abandoned by its own IES2 timeout rather than by this packet, which is why there is no inbound-state lookup here.

      As this packet was essentially unauthenticated (i.e. intro key, not session key) we just log it as it could be spoofed.

      SSU 2.

      Parameters:
      from - the unauthenticated sender, logged only as it may be spoofed
    • isPeerBanned

      private boolean isPeerBanned(RemoteHostId hostId)
      Is the peer on the blocklist or banlist?
      Parameters:
      hostId - the peer's address and hash to test
      Returns:
      true if the peer's IP is blocklisted or its hash is banlisted
    • isPeerBanned

      private boolean isPeerBanned(RemoteHostId hostId, String context)
      Is the peer on the blocklist or banlist?
      Parameters:
      hostId - the peer's address and hash to test
      context - what to call the peer in the log line naming the match
      Returns:
      true if the peer's IP is blocklisted or its hash is banlisted
    • isPeerBanned

      private boolean isPeerBanned(InboundEstablishState2 state)
      Is the peer on the blocklist or banlist?
      Parameters:
      state - the inbound attempt whose peer to test
      Returns:
      true if the peer's IP is blocklisted or its hash is banlisted
    • isPeerBanned

      private boolean isPeerBanned(OutboundEstablishState2 state)
      Is the peer on the blocklist or banlist?
      Parameters:
      state - the outbound attempt whose peer to test
      Returns:
      true if the peer's IP is blocklisted or its hash is banlisted
    • receiveData

      PeerState receiveData(OutboundEstablishState state)
      A data packet arrived on an outbound connection being established, which means its complete (yay!). This is a blocking call, more than I'd like...
      Parameters:
      state - the outbound attempt that completed, removed from the pending maps
      Returns:
      the new PeerState
    • locked_admitQueued

      private int locked_admitQueued()
      Move pending outbound messages from _queuedOutbound into active outbound establishment states. This processes one peer at a time until concurrency limits are reached.
      Returns:
      number of peers admitted from the queue
    • notifyActivity

      private void notifyActivity()
      Records that an establishment attempt happened.
    • handleCompletelyEstablished

      private void handleCompletelyEstablished(InboundEstablishState state)
      Ok, fully received, add it to the established cons and queue up a netDb store to them.
      Parameters:
      state - the inbound attempt that just completed, an SSU2 state, ignored if it is already marked complete
    • sendInboundComplete

      private void sendInboundComplete(PeerState peer)
      Send our info immediately, bundled with the ACK of packet 0.

      The two messages share a packet, so this stays separate from sendAck0() rather than being folded into it: sendAck0() may run before the PeerState exists, and only this path needs the peer's RouterInfo.

      Parameters:
      peer - the newly established peer to send our RouterInfo to
    • handleCompletelyEstablished

      private PeerState handleCompletelyEstablished(OutboundEstablishState state)
      Ok, fully received, add it to the established cons and send any queued messages.
      Parameters:
      state - the outbound attempt that just completed, an SSU2 state
      Returns:
      the new PeerState, or null if the state has no remote identity
    • getOurInfo

      public DatabaseStoreMessage getOurInfo()
      A database store message with our router info.
      Returns:
      non-null
    • sendCreated

      private void sendCreated(InboundEstablishState state)
      This handles both initial send and retransmission of Session Created, and, for SSU2, send of Retry. Retry is never retransmitted except in response to a retransmitted Token Request.

      This may be called more than once.

      Caller must synch on state.

      Parameters:
      state - the inbound attempt to answer, an SSU2 state whose current inbound state selects which packet is sent
    • sendRequest

      private void sendRequest(OutboundEstablishState state)
      This handles both initial send and retransmission of SessionRequest, and, for SSU2, initial send and retransmission of Token Request.

      This may be called more than once.

      Caller must synch on state.

      Parameters:
      state - the outbound attempt to drive, an SSU2 state whose current outbound state selects whether a Token Request or a Session Request is sent
    • handlePendingIntro

      private void handlePendingIntro(OutboundEstablishState state)
      Send RelayRequests to multiple introducers. This may be called multiple times, it sets the nonce the first time only Caller should probably synch on state.

      SSU 2.

      Parameters:
      state - the outbound state to walk through the introducers of; must be an OutboundEstablishState2 with at least one valid v2 introducer
    • sendRelayRequest

      private boolean sendRelayRequest(long tag, PeerState2 bob, OutboundEstablishState charlie)
      We are Alice, send a RelayRequest to Bob.

      SSU 2 only.

      Parameters:
      tag - the per-tunnel introducer tag taken from Charlie's address entry, echoed in the signed request data
      bob - the already-connected introducer's session to ask
      charlie - must be SSU2
      Returns:
      true if the RelayRequest was queued; false if we have no address to send from or the payload could not be signed or written
    • receiveRelayResponse

      void receiveRelayResponse(PeerState2 bob, long nonce, int code, byte[] data)
      We are Alice, we sent a RelayRequest to Bob and got a RelayResponse back. Time and version already checked by caller.

      SSU 2 only.

      Parameters:
      bob - the introducer's established session, we are answering its relay
      nonce - matches the live introduction we sent, keys the pending outbound state
      code - 0 if Charlie accepted, 1-63 if the introducer rejected, 64+ if Charlie rejected
      data - including nonce, including token if code == 0
    • receiveHolePunch

      void receiveHolePunch(RemoteHostId id, UDPPacket packet)
      Called from PacketHandler. Accelerate response to RelayResponse if we haven't sent it yet.

      SSU 2 only.

      Parameters:
      id - non-null
      packet - header already decrypted
    • isValidHolePunchResponse

      private boolean isValidHolePunchResponse(EstablishmentManager.HPCallback cb, long rcvConnID, long now, RemoteHostId id)
      Validate the decrypted HolePunch payload: response code, clock skew, the nonce echoing the receiving connection ID, the response time, and the version.
      Parameters:
      cb - payload callback results
      rcvConnID - connection ID this packet was sent to
      now - the packet receipt time in milliseconds
      id - the RemoteHostId of the sending peer
      Returns:
      true if all checks pass
    • findHolePunchState

      private OutboundEstablishState findHolePunchState(RemoteHostId id, long nonce)
      Find the outbound establish state this HolePunch belongs to.
      Parameters:
      id - the RemoteHostId of the sending peer
      nonce - the nonce from the validated response
      Returns:
      the state, or null if unknown
    • verifyIntroducerSignature

      private byte[] verifyIntroducerSignature(OutboundEstablishState state, OutboundEstablishState2 state2, EstablishmentManager.HPCallback cb)
      Validate the introducer signature on the HolePunch, by trying each introducer in a state that may have signed it.
      Parameters:
      state - outbound establish state
      state2 - SSU2-specific state, for intro bookkeeping
      cb - payload callback results
      Returns:
      the signed portion of the response data (everything but the trailing token), or null if Charlie's RouterInfo is unknown or no introducer signature validated
    • banAndFailState

      private void banAndFailState(OutboundEstablishState state, String reason, long banDuration)
      Ban the peer and fail the outbound establish state for bad introduction data.
      Parameters:
      state - the outbound establish state
      reason - the human-readable cause recorded in the banlist and the ban log
      banDuration - ban duration in ms
    • validateHolePunchAddress

      private int validateHolePunchAddress(OutboundEstablishState state, RemoteHostId id, byte[] data)
      Validate the IP and port Charlie claims in the signed data. A port mismatch is tolerated (use the source port, Charlie may be behind a symmetric NAT); an invalid source port is not.
      Parameters:
      state - the outbound establish state
      id - the actual sender of the HolePunch
      data - the signed portion of the response
      Returns:
      the validated port (possibly the sender's source port), or -1 if the data is invalid
    • updateHolePunchState

      private void updateHolePunchState(OutboundEstablishState state, OutboundEstablishState2 state2, byte[] data, int port, long now, long token)
      Update the state with the validated Charlie address, and swap the RemoteHostId the state is indexed under if this was a HolePunch before the RelayResponse.
      Parameters:
      state - the outbound establish state
      state2 - SSU2-specific state
      data - the signed portion of the response
      port - the validated port
      now - the packet receipt time in milliseconds
      token - the token from the end of the full response
    • isValid

      boolean isValid(byte[] ip, int port)
      Are IP and port valid? This is only for checking the relay response. Allow IPv6 as of 0.9.50. Refuse anybody in the same /16.
      Parameters:
      ip - the address from the relay response, IPv4 or IPv6, may be null
      port - the claimed port, must be a valid 1-65535
      Returns:
      true if the address is routable, blocklist-free and not in our own /16
    • sendConfirmation

      private void sendConfirmation(OutboundEstablishState state)
      SSU 2. For SSU 2, it contains a full router info, so it may be fragmented.

      Caller must synch on state.

      Parameters:
      state - the outbound attempt to confirm, an SSU2 state, failed if its SessionCreated does not validate
    • handleInbound

      private long handleInbound()
      Drive through the inbound establishment states, adjusting one of them as necessary. Called from Establisher thread only.
      Returns:
      next requested time or Long.MAX_VALUE
    • handleOutbound

      private long handleOutbound()
      Drive through the outbound establishment states, adjusting one of them as necessary. Called from Establisher thread only.
      Returns:
      next requested time or Long.MAX_VALUE
    • processExpired

      private void processExpired(OutboundEstablishState outboundState)
      Caller should probably synch on outboundState.
      Parameters:
      outboundState - the outbound attempt that timed out; dropped from the pending maps, and its queued messages failed unless the session is already confirmed
    • processExpired

      private void processExpired(InboundEstablishState inboundState)
      Caller should probably synch on inboundState.
      Parameters:
      inboundState - the inbound attempt that timed out; its address is added to the inbound ban map and its queued messages are failed
    • addOutboundToken

      public void addOutboundToken(RemoteHostId peer, long token, long expires)
      Remember a token that can be used later to connect to the peer.
      Parameters:
      peer - the peer's address and port, which keys the stored token
      token - nonzero token value to store
      expires - absolute expiry as milliseconds since the epoch
    • getOutboundToken

      public long getOutboundToken(RemoteHostId peer)
      Token to connect to the peer.
      Parameters:
      peer - the peer's address and port, consuming any stored token
      Returns:
      0 if none available
    • isConnecting

      public boolean isConnecting(Hash dest)
      Check if an outbound SSU handshake is in progress for the given peer.
      Parameters:
      dest - the remote RouterIdentity hash, as stored in the outbound-by-hash map
      Returns:
      true if we are currently trying to establish an outbound connection
    • ipChanged

      public void ipChanged(boolean isIPv6)
      Remove our tokens for this length.
      Parameters:
      isIPv6 - true if the new address is 16 bytes, false if 4
    • portChanged

      public void portChanged()
      Remove all tokens.
    • inboundTokenLifetimeStat

      private RateStat inboundTokenLifetimeStat()
      Resolves the "udp.inboundTokenLifetime" RateStat, caching the handle.

      The stat is created in this class's constructor and never replaced, so after the first successful lookup this is a single volatile read. A stat that is not registered yet is not cached, so a later call retries.

      Returns:
      the cached stat, or null if it is not registered yet
    • getInboundToken

      public EstablishmentManager.Token getInboundToken(RemoteHostId peer)
      Token that can be used later for the peer to connect to us.
      Parameters:
      peer - the peer's address and port, at most one live token per peer
      Returns:
      the inbound token
    • getInboundToken

      public EstablishmentManager.Token getInboundToken(RemoteHostId peer, long expiration)
      Token that can be used later for the peer to connect to us.
      Parameters:
      peer - the peer's address and port, at most one live token per peer
      expiration - time from now, will be reduced if necessary based on cache eviction time.
      Returns:
      non-null
    • isInboundTokenValid

      public boolean isInboundTokenValid(RemoteHostId peer, long token)
      Is the token from this peer valid?
      Parameters:
      peer - the peer's address and port, keying the stored token
      token - the offered value, a match consumes the stored token; 0 always fails
      Returns:
      true if it matched and has not expired
    • loadTokens

      private void loadTokens()
      Not threaded, because we're holding the token cache locks anyway.

      Format:

       4 ourIPv4addr ourIPv4port
       6 ourIPv6addr ourIPv6port
       I addr port token exp
       O addr port token exp
      
    • saveTokens

      private void saveTokens()
      Write unused intro tokens to disk.
    • shouldSuppressException

      static boolean shouldSuppressException(Throwable t)
      Determines whether the given Throwable (or any of its causes) contains a message that should be suppressed from logging.
      Parameters:
      t - the Throwable to check
      Returns:
      true if the exception should be suppressed, false otherwise
    • parseReason

      public static String parseReason(int reasonCode)
      The reason string for an SSU termination code.
      Parameters:
      reasonCode - the 1-byte termination code, one of the SSU2Util.REASON_ constants
      Returns:
      the reason text, or "Unknown error" for an unrecognized code