java.lang.Object
net.i2p.router.transport.udp.InboundEstablishState
net.i2p.router.transport.udp.InboundEstablishState2
All Implemented Interfaces:
SSU2Payload.PayloadCallback

class InboundEstablishState2extends InboundEstablishState implements SSU2Payload.PayloadCallback
Data for a new connection being established, where the remote peer has initiated the connection with us. In other words, they are Alice and we are Bob.

SSU2 only.

  • Field Details

    • _banLogger

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

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

      private final InetSocketAddress _aliceSocketAddress
      Alice's socket address.
    • _rcvConnID

      private final long _rcvConnID
      Connection id we receive with.
    • _sendConnID

      private final long _sendConnID
      Connection id we send with.
    • _token

      private final long _token
      The handshake token echoed to Alice.
    • _handshakeState

      private final HandshakeState _handshakeState
      Handshake state for the ratchet keys.
    • _version

      private final int _version
      Version of the handshake.
    • _sendHeaderEncryptKey1

      private byte[] _sendHeaderEncryptKey1
      First header encryption key for sending.
    • _sendHeaderEncryptKey2

      private byte[] _sendHeaderEncryptKey2
      Second header encryption key for sending.
    • _rcvHeaderEncryptKey2

      private byte[] _rcvHeaderEncryptKey2
      Second header encryption key for receiving.
    • _sessCrForReTX

      private byte[] _sessCrForReTX
      Session Created packet held for retransmission.
    • _sessConfFragments

      private byte[][] _sessConfFragments
      Session Confirmed fragments for retransmission.
    • _timeReceived

      private long _timeReceived
      When the handshake started.
    • _skew

      private long _skew
      Our clock skew to Alice, in ms.
    • _mtu

      private int _mtu
      The MTU agreed for the new peer.
    • _pstate

      private PeerState2 _pstate
      The peer state being built, once created.
    • _queuedDataPackets

      private List<UDPPacket> _queuedDataPackets
      Data packets queued before the handshake finished.
    • ENFORCE_TOKEN

      private static final boolean ENFORCE_TOKEN
      True to enforce the handshake token.
      See Also:
    • MAX_SKEW

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

      private static final String MIN_RELAY_VERSION
      First version supporting SSU2 relays.
      See Also:
    • YGGDRASIL_FIRST_BYTE_MASK

      private static final int YGGDRASIL_FIRST_BYTE_MASK
      A yggdrasil IPv6 address starts with 0x02 or 0x03 (first byte 0b0000001x).
      See Also:
    • YGGDRASIL_FIRST_BYTE

      private static final int YGGDRASIL_FIRST_BYTE
      First byte of the Yggdrasil protocol.
      See Also:
    • IPV6_SELF_PREFIX_BYTES

      private static final int IPV6_SELF_PREFIX_BYTES
      Bytes of the source IPv6 prefix that identify an address as this router's own.
      See Also:
    • SSU_V1_MTU_19_BUG

      private static final int SSU_V1_MTU_19_BUG
      SSU 1.0 mtu option value that maps to MIN_MTU; workaround for a bug in 1.9.0.
      See Also:
    • RI_SKEW_FUTURE_WINDOW

      private static final long RI_SKEW_FUTURE_WINDOW
      A RouterInfo published more than 2 minutes in the future is clock-skewed.
      See Also:
    • RI_SKEW_PAST_WINDOW

      private static final long RI_SKEW_PAST_WINDOW
      A RouterInfo published more than 1 hour in the past is clock-skewed.
      See Also:
    • INTRO_KEEP

      static final int INTRO_KEEP
      Keep the introduction request; createPeerState() sends the relay tag with ACK 0.
      See Also:
    • INTRO_BLOCKED

      static final int INTRO_BLOCKED
      Drop it: a temporary sent port or no introduction support on the peer's family.
      See Also:
    • INTRO_VERSION

      static final int INTRO_VERSION
      Drop it: the peer's version is too old to relay to.
      See Also:
    • INTRO_OPPORTUNISTIC

      static final int INTRO_OPPORTUNISTIC
      Drop it: the peer advertises itself reachable; relaying is not required.
      See Also:
  • Constructor Details

    • InboundEstablishState2

      public InboundEstablishState2(RouterContext ctx, UDPTransport transport, UDPPacket packet) throws GeneralSecurityException
      Start a new handshake with the given incoming packet, which must be a Session Request or Token Request.

      Caller must then check getState() and build a Retry or Session Created in response.

      Parameters:
      ctx - the router context
      transport - the UDP transport this inbound handshake belongs to
      packet - with all header encryption removed, either a SessionRequest OR a TokenRequest.
      Throws:
      GeneralSecurityException - if the connection IDs in the packet are identical
  • Method Details

    • getVersion

      public int getVersion()
      Report the SSU 2 version negotiated with the peer.
      Overrides:
      getVersion in class InboundEstablishState
      Returns:
      the negotiated protocol version number
    • getMTU

      public int getMTU()
      The MTU to use for packets to this peer: his MTU, as learned from his address options or from the size of the request he sent, capped by our own when we know it. The version 4 handshake needs an MTU of PeerState2.MIN_MLKEM768_IPV4_MTU or PeerState2.MIN_MLKEM768_IPV6_MTU, and the padding in a session created is sized from this.
      Returns:
      the MTU in bytes, 0 if unknown
    • processPayload

      private void processPayload(byte[] payload, int offset, int length, boolean isHandshake) throws GeneralSecurityException
      Processes one handshake payload.
      Parameters:
      payload - the packet payload
      offset - where the handshake data starts
      length - the handshake data length
      isHandshake - true for a handshake packet
      Throws:
      GeneralSecurityException - if the decryption or MAC fails
    • gotDateTime

      public void gotDateTime(long time)
      Payload callback: got a DateTime block.
      Specified by:
      gotDateTime in interface SSU2Payload.PayloadCallback
      Parameters:
      time - time in milliseconds since epoch
    • gotOptions

      public void gotOptions(byte[] options, boolean isHandshake)
      Got an Options block.
      Specified by:
      gotOptions in interface SSU2Payload.PayloadCallback
      Parameters:
      options - the option data
      isHandshake - true only for message 3 part 2
    • gotRI

      public void gotRI(RouterInfo ri, boolean isHandshake, boolean flood) throws DataFormatException
      For most errors here we throw a RIException with a reason code, which is caught in processPayload(byte[], int, int, boolean) to create a PeerStateDestroyed and send a termination with that reason.

      Plain DataFormatExceptions indicate you may not respond in-session.

      Specified by:
      gotRI in interface SSU2Payload.PayloadCallback
      Parameters:
      ri - will already be validated
      isHandshake - true only for message 3 part 2
      flood - true if this is a floodfill router
      Throws:
      DataFormatException - if the RouterInfo block is malformed
    • selectSessionAddress

      static InboundEstablishState2.AddressSelection selectSessionAddress(List<RouterAddress> addrs, boolean isIPv6, byte[] aliceIP)
      Choose the RouterInfo address this session negotiates with, given the sender's IP family, so the caller can determine the MTU and keys. The rules mirror the SSU2 handshake:
      • with several addresses, a pure SSU 1 address (no "s" option) is skipped
      • an address is skipped when its host is not compatible with the sender's IP family, unless the capacity bit marks it usable
      • an address is skipped when it advertises the sender's own IP (yggdrasil addresses and a matching IPv6 source prefix cannot be ourselves)
      • an address that advertises a different IP than the sender's source address is selected but flagged, so the caller can ban it after the signature is verified
      Parameters:
      addrs - the addresses from the peer's RouterInfo, in preference order
      isIPv6 - true if the sender connected over IPv6
      aliceIP - the sender's source IP bytes (4 for IPv4, 16 for IPv6)
      Returns:
      the selection; ra is null when no address qualified
    • parseMTU

      static int parseMTU(RouterAddress ra, boolean isIPv6) throws InboundEstablishState2.RIException
      Resolve the session MTU from the peer's "mtu" option. Pure decision logic: a missing or unparsable value falls back to the per-style/family default, the 1.9.0 workaround maps 1276 to MIN_MTU for SSU 1, and any other value is clamped into the legal range for the negotiated style and IP family. A declared MTU below MIN_MTU (other than the workaround) is rejected.
      Parameters:
      ra - the negotiated SSU2/SSU address
      isIPv6 - true for an IPv6 session
      Returns:
      the effective MTU
      Throws:
      InboundEstablishState2.RIException - with REASON_OPTIONS if a declared MTU is below MIN_MTU
    • storeFailureReason

      static int storeFailureReason(long now, long published)
      Classify a RouterInfo store failure - generally an expired or future-dated RouterInfo that the netDb rejected - into a termination reason.
      Parameters:
      now - current clock time
      published - the RI's publication date
      Returns:
      REASON_SKEW when the publication date is outside the accepted skew window, otherwise REASON_MSG3
    • parseSessionKeys

      static byte[] parseSessionKeys(RouterAddress ra, byte[] publicKey) throws DataFormatException
      Decode and verify the SSU2 IKey/S options of the negotiated RouterInfo address and verify that the peer's S key matches the X25519 public key already derived from the handshake. Throws before any session state is modified, so a malformed or dishonest RouterInfo cannot corrupt the established keys.
      Parameters:
      ra - the negotiated address; must carry the "i" and "s" options
      publicKey - the X25519 public key from _handshakeState
      Returns:
      the verified IKey, to install as the send header key
      Throws:
      DataFormatException - with a type-specific message on any bad key or an S/public-key mismatch
    • decideIntroduction

      static int decideIntroduction(String version, String caps, boolean sentPortOk, boolean canIntroduce, boolean randomKeep)
      Decide whether a deferred relay-tag request survives once the peer's RouterInfo is known. Pure decision logic so the branch table is testable:
      • we cannot relay while our sent port is a temporary one or the transport does not support introduction on the peer's IP family
      • we do not relay to routers running older than MIN_RELAY_VERSION
      • when the peer already advertises itself reachable we only relay on a 1-in-4 draw - the peer is likely starting up and simply unaware of its reachability
      Parameters:
      version - the peer's RouterInfo version string
      caps - the peer's RouterInfo capabilities, or null
      sentPortOk - true if our sent port is a real, stable port (at least 1024)
      canIntroduce - true if the transport can introduce on the peer's IP family
      randomKeep - the pre-rolled 1-in-4 opportunistic keep draw
      Returns:
      INTRO_KEEP, INTRO_BLOCKED, INTRO_VERSION or INTRO_OPPORTUNISTIC
    • validateRouterInfo

      private void validateRouterInfo(RouterInfo ri, RouterAddress ra, String mismatchMessage) throws InboundEstablishState2.RIException
      Validates the RouterInfo for banning conditions and throws RIException if any issue is found. Checks: - Valid publication date - Banned status - Network ID match - IP match - SSU2 version - Old/slow router status.
      Parameters:
      ri - the received RouterInfo to validate
      ra - the peer's selected RouterAddress for this session
      mismatchMessage - address mismatch warning from the address selection, null if none
      Throws:
      InboundEstablishState2.RIException - if the RouterInfo fails any of the checks above
    • gotRIFragment

      public void gotRIFragment(byte[] data, boolean isHandshake, boolean flood, boolean isGzipped, int frag, int totalFrags)
      Got an RI fragment block.
      Specified by:
      gotRIFragment in interface SSU2Payload.PayloadCallback
      Parameters:
      data - is first gzipped and then fragmented
      isHandshake - true only for message 3 part 2
      flood - true if this is a floodfill router
      isGzipped - true if the data is gzipped
      frag - fragment number (0-based)
      totalFrags - total number of fragments
    • gotAddress

      public void gotAddress(byte[] ip, int port)
      Got an Address block.
      Specified by:
      gotAddress in interface SSU2Payload.PayloadCallback
      Parameters:
      ip - IP address bytes
      port - the port number to send
    • gotRelayTagRequest

      public void gotRelayTagRequest()
      Got a RelayTagRequest block.
      Specified by:
      gotRelayTagRequest in interface SSU2Payload.PayloadCallback
    • gotRelayTag

      public void gotRelayTag(long tag)
      Got a RelayTag block.
      Specified by:
      gotRelayTag in interface SSU2Payload.PayloadCallback
      Parameters:
      tag - the relay tag
    • gotRelayRequest

      public void gotRelayRequest(byte[] data)
      Got a RelayRequest block.
      Specified by:
      gotRelayRequest in interface SSU2Payload.PayloadCallback
      Parameters:
      data - excludes flag, includes signature
    • gotRelayResponse

      public void gotRelayResponse(int status, byte[] data)
      Got a RelayResponse block.
      Specified by:
      gotRelayResponse in interface SSU2Payload.PayloadCallback
      Parameters:
      status - 0 = accept, 1-255 = reject
      data - excludes flag, includes signature
    • gotRelayIntro

      public void gotRelayIntro(Hash aliceHash, byte[] data)
      Got a RelayIntro block.
      Specified by:
      gotRelayIntro in interface SSU2Payload.PayloadCallback
      Parameters:
      aliceHash - hash of the introducer
      data - excludes flag, includes signature
    • gotPeerTest

      public void gotPeerTest(int msg, int status, Hash h, byte[] data)
      Got a PeerTest block.
      Specified by:
      gotPeerTest in interface SSU2Payload.PayloadCallback
      Parameters:
      msg - 1-7
      status - 0 = accept, 1-255 = reject
      h - Alice or Charlie hash for msg 2 and 4, null for msg 1, 3, 5-7
      data - excludes flag, includes signature
    • gotToken

      public void gotToken(long token, long expires)
      Got a Token block.
      Specified by:
      gotToken in interface SSU2Payload.PayloadCallback
      Parameters:
      token - the session token to attach to the next peer test
      expires - token expiration time in milliseconds
    • gotI2NP

      public void gotI2NP(I2NPMessage msg)
      Got an I2NP block.
      Specified by:
      gotI2NP in interface SSU2Payload.PayloadCallback
      Parameters:
      msg - the parsed I2NP message
    • gotFragment

      public void gotFragment(byte[] data, int off, int len, long messageID, int frag, boolean isLast) throws DataFormatException
      Got a Fragment block.
      Specified by:
      gotFragment in interface SSU2Payload.PayloadCallback
      Parameters:
      data - buffer containing fragment data
      off - offset in data
      len - length of data to copy
      messageID - unique message identifier
      frag - fragment number (0-based)
      isLast - whether this is the last fragment
      Throws:
      DataFormatException - if the fragment is malformed
    • gotACK

      public void gotACK(long ackThru, int acks, byte[] ranges)
      Got an ACK block.
      Specified by:
      gotACK in interface SSU2Payload.PayloadCallback
      Parameters:
      ackThru - highest contiguous sequence number acknowledged
      acks - number of additional acknowledgments beyond ackThru
      ranges - null if none
    • gotTermination

      public void gotTermination(int reason, long count)
      Got a Termination block.
      Specified by:
      gotTermination in interface SSU2Payload.PayloadCallback
      Parameters:
      reason - termination reason code
      count - in theory could wrap around to negative, but very unlikely
    • gotPathChallenge

      public void gotPathChallenge(RemoteHostId from, byte[] data)
      Got a PathChallenge block.
      Specified by:
      gotPathChallenge in interface SSU2Payload.PayloadCallback
      Parameters:
      from - null if unknown
      data - the challenge bytes, which must be copied out
    • gotPathResponse

      public void gotPathResponse(RemoteHostId from, byte[] data)
      Got a PathResponse block.
      Specified by:
      gotPathResponse in interface SSU2Payload.PayloadCallback
      Parameters:
      from - null if unknown
      data - the response bytes, which must be copied out
    • fail

      public void fail()
      Overridden from SSU1 to destroy the handshake state.
      Overrides:
      fail in class InboundEstablishState
    • getSendConnID

      public long getSendConnID()
      The SSU2 send connection ID.
      Returns:
      the 64-bit connection ID used on our outbound packets
    • getRcvConnID

      public long getRcvConnID()
      The rcv conn ID.
      Returns:
      the 64-bit connection ID used by the peer on its packets
    • getToken

      public long getToken()
      Read the token this session presented to the peer.
      Returns:
      the SessionRequest token value
    • getNextToken

      public EstablishmentManager.Token getNextToken()
      Look up the next inbound establishment token for this session's peer.
      Returns:
      the token, or null for an IPv4 peer behind a symmetric NAT, where a fresh token cannot be attributed to the correct session
    • getHandshakeState

      public HandshakeState getHandshakeState()
      Read the handshake progress for this session.
      Returns:
      the current handshake state
    • getSendHeaderEncryptKey1

      public byte[] getSendHeaderEncryptKey1()
      First header encryption key for packets sent to the peer.
      Returns:
      the key bytes
    • getRcvHeaderEncryptKey1

      public byte[] getRcvHeaderEncryptKey1()
      First header encryption key for packets received from the peer.
      Returns:
      the key bytes
    • getSendHeaderEncryptKey2

      public byte[] getSendHeaderEncryptKey2()
      Second header encryption key for packets sent to the peer.
      Returns:
      the key bytes
    • getRcvHeaderEncryptKey2

      public byte[] getRcvHeaderEncryptKey2()
      Second header encryption key for packets received from the peer.
      Returns:
      the key bytes
    • getSentAddress

      public InetSocketAddress getSentAddress()
      Read the peer address this session is talking to.
      Returns:
      the address the peer connected from
    • createdPacketSent

      public void createdPacketSent()
      Record that we sent the SessionCreated packet.
      Overrides:
      createdPacketSent in class InboundEstablishState
    • retryPacketSent

      public void retryPacketSent()
      Note that we just sent a Retry packet.
    • receiveSessionOrTokenRequestAfterRetry

      public void receiveSessionOrTokenRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException
      All exceptions thrown from here will be fatal. fail() will be called before throwing.
      Parameters:
      packet - the retried Session Request or Token Request, header encryption removed
      Throws:
      GeneralSecurityException - if the packet fails address, connection ID, token, version or clock-skew validation; fail() has already been called
    • locked_receiveSessionOrTokenRequestAfterRetry

      private void locked_receiveSessionOrTokenRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException
      Process a session or token request after a retry, with the lock held.
      Parameters:
      packet - the repeated SessionRequest or TokenRequest packet from the peer
      Throws:
      GeneralSecurityException - if the source address does not match the initiator's, or decrypting or validating the request fails
    • receiveSessionConfirmed

      public PeerState2 receiveSessionConfirmed(UDPPacket packet)
      Receive the last messages in the handshake, and create the PeerState. If the message is fragmented, store the data for reassembly and return, unless this was the last one.

      Exceptions thrown from here are fatal.

      Parameters:
      packet - the SessionConfirmed message, header encryption removed
      Returns:
      the new PeerState2 if are done, may also be retrieved from getPeerState(), or null if more fragments to go
    • locked_receiveSessionConfirmed

      private PeerState2 locked_receiveSessionConfirmed(UDPPacket packet) throws GeneralSecurityException
      Process SessionConfirmed and create the peer state, with the lock held.
      Parameters:
      packet - the SessionConfirmed packet from the peer
      Returns:
      the newly created peer state for the established session
      Throws:
      GeneralSecurityException - if the state is wrong for a confirmation, the source address does not match, or decrypting or validating the confirmation fails
    • createPeerState

      private void createPeerState()
      Creates the PeerState and stores in _pstate. Called from gotRI(RouterInfo, boolean, boolean) so that we can pass any I2NP messages or fragments immediately to the PeerState.
    • createPeerStateDestroyed

      private PeerStateDestroyed createPeerStateDestroyed(int reason)
      Creates a PeerStateDestroyed after msg 3 failure, so we can send a termination and deal with subsequent in-session messages.
      Parameters:
      reason - the SSU2 termination reason code recorded in the destroyed state
      Returns:
      the destroyed peer state holding the session's chaining keys
    • createdPacketSent

      public void createdPacketSent(DatagramPacket pkt)
      Note that we just sent the SessionCreated packet, and save it for retransmission.
      Parameters:
      pkt - the SessionCreated datagram that was just sent, saved for retransmission
    • getRetransmitSessionCreatedPacket

      public UDPPacket getRetransmitSessionCreatedPacket()
      The retransmitted SessionCreated packet, or null if not sent or already got the session confirmed.
      Returns:
      null if not sent or already got the session confirmed
    • getPeerState

      public PeerState2 getPeerState()
      The completed PeerState, or null if we have not received the session confirmed.
      Returns:
      null if we have not received the session confirmed
    • queuePossibleDataPacket

      public void queuePossibleDataPacket(UDPPacket packet)
      Queue a data packet received before the session completed.
      Parameters:
      packet - with header still encrypted
    • shouldSuppressException

      private 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
    • toShortString

      String toShortString()
      Compact one-line establish-state identifier for log output: alice address and current state. No lifetime or IDs, no newlines.
      Returns:
      the alice address and state, on one line
    • toString

      public String toString()
      A string summary of this state.
      Overrides:
      toString in class InboundEstablishState
      Returns:
      debug string with peer address, lifetime, relay tag, and state