java.lang.Object
net.i2p.router.transport.ntcp.NTCPConnection
All Implemented Interfaces:
Closeable, AutoCloseable

public class NTCPConnectionextends Object implements Closeable
Coordinates a connection to a single peer using NTCP 1 or NTCP 2 protocols. Manages the complete connection lifecycle including establishment, message encryption/decryption, bandwidth management, and graceful shutdown.

Public only for UI peers page. Not a public API, not for external use.

  • Field Details

    • _context

      private final RouterContext _context
      The router running this transport.
    • _log

      private final Log _log
      Logger for this class.
    • _banLogger

      private static BanLogger _banLogger
      Logger for peers banned by this transport.
    • _chan

      private volatile SocketChannel _chan
      The socket channel, null once closed.
    • _conKey

      private volatile SelectionKey _conKey
      The selector key for this connection.
    • _inboundListener

      private final FIFOBandwidthLimiter.CompleteListener _inboundListener
      Bandwidth callback fired after inbound reads.
    • _outboundListener

      private final FIFOBandwidthLimiter.CompleteListener _outboundListener
      Bandwidth callback fired after outbound writes.
    • _readBufs

      private final Queue<ByteBuffer> _readBufs
      Queue of ByteBuffer containing data we have read and are ready to process, oldest first. Lockless. Reads are paused at MAX_READ_BUFS entries so a peer that sends faster than we process applies backpressure to the socket instead of growing the queue without bound; the pause lifts once the queue has drained.
    • MAX_READ_BUFS

      private static final int MAX_READ_BUFS
      Read buffers queued before the socket read interest is dropped (8 KB each), so one connection can hold at most about 256 KB of read-ahead.
      See Also:
    • _readPaused

      private volatile boolean _readPaused
      True while read interest is cleared because _readBufs is full.
    • MAX_WRITE_BUFS

      private static volatile int MAX_WRITE_BUFS
      List of ByteBuffers containing fully populated and encrypted data, ready to write, and already cleared through the bandwidth limiter. Unbounded and lockless.
    • _writeBufs

      private final Queue<ByteBuffer> _writeBufs
      Buffers queued for writing.
    • _bwInRequests

      private final Set<FIFOBandwidthLimiter.Request> _bwInRequests
      Requests that were not granted immediately.
    • _bwOutRequests

      private final Set<FIFOBandwidthLimiter.Request> _bwOutRequests
      Outstanding outbound bandwidth requests.
    • _establishedOn

      private long _establishedOn
      Time the handshake completed.
    • _establishState

      private volatile EstablishState _establishState
      Phase of the handshake.
    • _transport

      private final NTCPTransport _transport
      The transport that owns this connection.
    • _isInbound

      private final boolean _isInbound
      True if the peer connected to us.
    • _closed

      private final AtomicBoolean _closed
      True once the connection is closed.
    • _remAddr

      private final RouterAddress _remAddr
      The peer's address as configured.
    • _remotePeer

      private RouterIdentity _remotePeer
      The peer's identity, once established.
    • _clockSkew

      private long _clockSkew
      Our clock minus the peer's, in seconds.
    • _outbound

      private final PriBlockingQueue<OutNetMessage> _outbound
      Pending unprepared OutNetMessage instances.
    • _currentOutbound

      private final List<OutNetMessage> _currentOutbound
      Current prepared OutNetMessages, or empty - synchronize on _writeLock.
    • _curReadState

      private NTCPConnection.ReadState _curReadState
      Current partially read I2NP message.
    • _messagesRead

      private final AtomicInteger _messagesRead
      Messages read on this connection.
    • _messagesWritten

      private final AtomicInteger _messagesWritten
      Messages written on this connection.
    • _lastSendTime

      private long _lastSendTime
      Time of the last write.
    • _lastReceiveTime

      private long _lastReceiveTime
      Time of the last read.
    • _establishmentRtt

      private volatile int _establishmentRtt
      Round trip time of the handshake that established this session, in ms, or 0 when it has not been measured.

      Both NTCP2 establishment paths already compute this to derive clock skew - now - getCreated() at the point the peer's timestamp arrives - but discarded it, so the transport had no latency to report and Transport.getEstimatedRTT(Hash) answered 0 for every NTCP2 peer. With that answer, every consumer of a direct-link latency reading stayed empty: the profile's first-hop RTT was never populated, and the low-latency flag never turned on for NTCP2 peers.

      Measured once, at establishment, and never updated afterwards: it describes the handshake this session was built on, and the profile-level smoothing in PeerProfile.setFirstHopRtt is what carries it forward. Recorded here rather than derived by the reader, so the value has exactly one source per session.

    • _establishmentRttTime

      private volatile long _establishmentRttTime
      When _establishmentRtt was measured, on the router clock, so a reader can tell a handshake-time reading from a current one.

      Kept beside the value rather than derived from session age, so the measurement's timestamp is the handshake's own and cannot drift if the reader is called at a different moment.

    • _lastRateUpdated

      private long _lastRateUpdated
      Time the byte rates were last computed.
    • _lastWriteBufsStat

      private long _lastWriteBufsStat
      Last time "ntcp.writeBufs.size" was recorded, to throttle the per-frame RateStat call on the write path (sample at most once per second). Best-effort throttle only; no _writeLock needed.
    • _created

      private final long _created
      Time the connection object was created.
    • _nextMetaTime

      private long _nextMetaTime
      Time before which metadata must not be sent, kept past establish.
    • _consecutiveZeroReads

      private final AtomicInteger _consecutiveZeroReads
      Reads returning zero in a row.
    • _readLock

      private final Object _readLock
      Lock guarding _curReadState, the current read state.
    • _writeLock

      private final Object _writeLock
      Lock guarding _currentOutbound, _outbound, _sendSipk1, _sendSipk2, _sendSipIV and _sender. _writeBufs is drained only by the pumper thread (ConcurrentLinkedQueue), so it no longer needs this lock.
    • _statLock

      private final Object _statLock
      Lock guarding the byte and rate counters: _bytesReceived, _bytesSent, _lastBytesReceived, _lastBytesSent, _sendBps and _recvBps.
    • _nextInfoTime

      private long _nextInfoTime
      How many consecutive sends were failed due to (estimated) send queue time.
    • _mayDisconnect

      private volatile boolean _mayDisconnect
      True once either side may disconnect.
    • _writeInterestPending

      private final AtomicBoolean _writeInterestPending
      True while a write-interest change awaits the selector.
    • _lastActiveTime

      private volatile long _lastActiveTime
      Time of the last read or write.
    • _lastZeroReadTime

      private volatile long _lastZeroReadTime
      Time of the last zero-length read.
    • STAT_UPDATE_TIME_MS

      private static final long STAT_UPDATE_TIME_MS
      How often send and receive rates are recomputed for the console peers page.
      See Also:
    • META_FREQUENCY

      private static final int META_FREQUENCY
      Interval between the pool test messages, longer than twice the pumper maximum idle time so it does not interfere with the idle timeout.
      See Also:
    • INFO_FREQUENCY

      private static final int INFO_FREQUENCY
      How often we send our routerinfo unsolicited.
      See Also:
    • BUFFER_SIZE

      static final int BUFFER_SIZE
      Why this is 16K, and where it is documented, good question? We claim we can do 32K datagrams so this is a problem. Needs to be fixed. But SSU can handle it? In the meantime, don't let the transport bid on big messages.
      See Also:
    • MAX_DATA_READ_BUFS

      private static final int MAX_DATA_READ_BUFS
      Size of the pooled read-buffer cache.
    • _dataReadBufs

      private static final ByteCache _dataReadBufs
      The cache supplying the data read buffers.
    • __connID

      private static final AtomicLong __connID
      Source of unique connection ids.
    • _connID

      private final long _connID
      Id of this connection, for logs.
    • NTCP2_MAX_MSG_SIZE

      static final int NTCP2_MAX_MSG_SIZE
      See spec. Max Noise payload 65535, minus 16 byte MAC and 3 byte block header. Includes 9-byte I2NP header.
      See Also:
    • PADDING_MAX

      private static final int PADDING_MAX
      Largest padding a single message may carry, in bytes.
      See Also:
    • SIP_IV_LENGTH

      private static final int SIP_IV_LENGTH
      Length of the SipHash IV, in bytes.
      See Also:
    • NTCP2_FAIL_READ

      private static final int NTCP2_FAIL_READ
      Cap on the junk read after a failed handshake, in bytes.
      See Also:
    • NTCP2_FAIL_TIMEOUT

      private static final long NTCP2_FAIL_TIMEOUT
      Time allowed for that junk read, in ms.
      See Also:
    • NTCP2_TERMINATION_CLOSE_DELAY

      private static final long NTCP2_TERMINATION_CLOSE_DELAY
      Delay before closing after a termination block, in ms.
      See Also:
    • NTCP2_PREFERRED_PAYLOAD_MAX

      private static final int NTCP2_PREFERRED_PAYLOAD_MAX
      Preferred cap for a combined payload, in bytes. Tunnel data messages are 1024 + 4 + 9 + 3 = 1040, so five fit; keeping combined messages small minimizes latency.
      See Also:
    • REASON_UNSPEC

      static final int REASON_UNSPEC
      Unspecified reason.
      See Also:
    • REASON_TERMINATION

      static final int REASON_TERMINATION
      Termination reason.
      See Also:
    • REASON_TIMEOUT

      static final int REASON_TIMEOUT
      Timeout reason.
      See Also:
    • REASON_AEAD

      static final int REASON_AEAD
      AEAD failure reason.
      See Also:
    • REASON_OPTIONS

      static final int REASON_OPTIONS
      Options mismatch reason.
      See Also:
    • REASON_SIGTYPE

      static final int REASON_SIGTYPE
      Signature type mismatch reason.
      See Also:
    • REASON_SKEW

      static final int REASON_SKEW
      Clock skew reason.
      See Also:
    • REASON_PADDING

      static final int REASON_PADDING
      Padding violation reason.
      See Also:
    • REASON_FRAMING

      static final int REASON_FRAMING
      Framing error reason.
      See Also:
    • REASON_PAYLOAD

      static final int REASON_PAYLOAD
      Payload error reason.
      See Also:
    • REASON_MSG1

      static final int REASON_MSG1
      Message 1 error reason.
      See Also:
    • REASON_MSG2

      static final int REASON_MSG2
      Message 2 error reason.
      See Also:
    • REASON_MSG3

      static final int REASON_MSG3
      Message 3 error reason.
      See Also:
    • REASON_FRAME_TIMEOUT

      static final int REASON_FRAME_TIMEOUT
      Frame timeout reason.
      See Also:
    • REASON_SIGFAIL

      static final int REASON_SIGFAIL
      Signature failure reason.
      See Also:
    • REASON_S_MISMATCH

      static final int REASON_S_MISMATCH
      S mismatch reason.
      See Also:
    • REASON_BANNED

      static final int REASON_BANNED
      Banned reason.
      See Also:
    • PADDING_MIN_DEFAULT_INT

      static final int PADDING_MIN_DEFAULT_INT
      Default minimum padding integer.
      See Also:
    • PADDING_MAX_DEFAULT_INT

      static final int PADDING_MAX_DEFAULT_INT
      Default maximum padding integer.
      See Also:
    • PADDING_MIN_DEFAULT

      private static final float PADDING_MIN_DEFAULT
      Default minimum padding, as a fraction of the message.
      See Also:
    • PADDING_MAX_DEFAULT

      private static final float PADDING_MAX_DEFAULT
      Default maximum padding, as a fraction of the message.
      See Also:
    • DUMMY_DEFAULT

      static final int DUMMY_DEFAULT
      Default dummy value.
      See Also:
    • DELAY_DEFAULT

      static final int DELAY_DEFAULT
      Default delay value.
      See Also:
    • OUR_PADDING

      private static final NTCP2Options OUR_PADDING
      The padding options we advertise.
    • MIN_PADDING_RANGE

      private static final int MIN_PADDING_RANGE
      Smallest padding range allowed.
      See Also:
    • MAX_PADDING_RANGE

      private static final int MAX_PADDING_RANGE
      Largest padding range allowed.
      See Also:
    • _paddingConfig

      private volatile NTCP2Options _paddingConfig
      The padding options in effect for this connection.
    • _version

      private int _version
      The NTCP version in use.
    • _sender

      private CipherState _sender
      Cipher state for outbound data.
    • _sendSipk1

      private long _sendSipk1
      First SipHash ratchet key for outbound data.
    • _sendSipk2

      private long _sendSipk2
      Second SipHash ratchet key for outbound data.
    • _sendSipIV

      private byte[] _sendSipIV
      SipHash IV for outbound data.
    • _blockPool

      private static final ThreadLocal<NTCPConnection.BlockPool> _blockPool
      Per-thread pool of payload blocks, avoiding allocator churn.
    • _bytesReceived

      private long _bytesReceived
      Bytes received so far, guarded by _statLock.
    • _bytesSent

      private long _bytesSent
      Bytes sent so far, guarded by _statLock.
    • _lastBytesReceived

      private long _lastBytesReceived
      _bytesReceived when we last updated the rate.
    • _lastBytesSent

      private long _lastBytesSent
      _bytesSent when we last updated the rate.
    • _sendBps

      private float _sendBps
      Current send rate, in bytes per second.
    • _recvBps

      private float _recvBps
      Current receive rate, in bytes per second.
    • BAN_RECHECK_MS

      static final long BAN_RECHECK_MS
      How long a connection's ban/blocklist verdict stays cached, in ms.

      isBanned() is called on every data-phase frame, and each call copies the remote IP and binary-searches the blocklist's range array before it looks the peer up in the banlist. Caching the verdict for a few seconds takes that work off the busiest receive path; a ban or block added while a connection is already up takes effect on the next check instead of the next frame.

      See Also:
    • _banned

      private volatile boolean _banned
      Cached ban/blocklist verdict, valid until _nextBanCheck.
    • _nextBanCheck

      private volatile long _nextBanCheck
      Clock time after which the cached verdict is stale, in ms.
    • _currentInterestOps

      private volatile int _currentInterestOps
      The interest ops currently set on the selector key.
  • Constructor Details

    • NTCPConnection

      public NTCPConnection(RouterContext ctx, NTCPTransport transport, SocketChannel chan, SelectionKey key)
      Create an inbound connected (though not established) NTCP connection. Caller MUST call transport.establishing(this) after construction. Caller MUST key.attach(this) after construction.
      Parameters:
      ctx - the router context
      transport - the NTCP transport
      chan - the socket channel
      key - the selection key
    • NTCPConnection

      public NTCPConnection(RouterContext ctx, NTCPTransport transport, RouterIdentity remotePeer, RouterAddress remAddr, int version) throws DataFormatException
      Create an outbound unconnected NTCP connection. Caller MUST call transport.establishing(this) after construction.
      Parameters:
      ctx - the router context
      transport - the NTCP transport
      remotePeer - the remote peer identity
      remAddr - the remote router address
      version - must be 2-5
      Throws:
      DataFormatException - if there's a problem with the address
    • NTCPConnection

      private NTCPConnection(RouterContext ctx, NTCPTransport transport, RouterAddress remAddr, boolean isIn)
      Base constructor for an inbound or outbound connection.
      Parameters:
      ctx - the router context
      transport - the NTCP transport
      remAddr - the remote router address, may be null for inbound
      isIn - true for inbound, false for outbound
  • Method Details

    • getMaxWriteBufs

      public static int getMaxWriteBufs()
      Current max write buffers per NTCP connection.
      Returns:
      the current max write buffers
    • getMaxReadBufs

      public static int getMaxReadBufs()
      Current max read buffers per NTCP connection.
      Returns:
      the current max read buffers
    • setMaxWriteBufs

      public static void setMaxWriteBufs(int max)
      Max write buffers per NTCP connection (called by Tuner).
      Parameters:
      max - the maximum write buffers
    • getChannel

      public SocketChannel getChannel()
      Valid for inbound; valid for outbound shortly after creation.
      Returns:
      the socket channel
    • getKey

      public SelectionKey getKey()
      Valid for inbound; valid for outbound shortly after creation.
      Returns:
      the selection key
    • setChannel

      public void setChannel(SocketChannel chan)
      Socket channel for this connection.
      Parameters:
      chan - the channel this connection reads and writes through
    • setKey

      public void setKey(SelectionKey key)
      Selection key for this connection.
      Parameters:
      key - the selection key registering this connection with the pumper
    • isInbound

      public boolean isInbound()
      Whether this is an inbound connection.
      Returns:
      true if inbound
    • isEstablished

      public boolean isEstablished()
      Whether the connection is established.
      Returns:
      true if established
    • isIPv6

      public boolean isIPv6()
      Whether the connection is over IPv6.
      Returns:
      true if IPv6
    • getRemoteIP

      public byte[] getRemoteIP()
      Return the remote peer's IP address.
      Returns:
      null if unknown
    • getRemotePort

      public int getRemotePort()
      Return the remote peer's UDP port.
      Returns:
      0 if unknown
    • getEstablishState

      EstablishState getEstablishState()
      Only valid during establishment; replaced with EstablishState.VERIFIED or FAILED afterwards.
      Returns:
      the establish state
    • getRemoteAddress

      public RouterAddress getRemoteAddress()
      Only valid for outbound; null for inbound.
      Returns:
      the remote address
    • getRemotePeer

      public RouterIdentity getRemotePeer()
      Valid for outbound; valid for inbound after handshake.
      Returns:
      the remote peer identity
    • setRemotePeer

      public void setRemotePeer(RouterIdentity ident)
      Valid for outbound; valid for inbound after handshake.
      Parameters:
      ident - the remote peer identity
    • getClockSkew

      public long getClockSkew()
      A positive number means our clock is ahead of theirs.
      Returns:
      the clock skew in seconds, positive if our clock runs ahead
    • getUptime

      public long getUptime()
      Connection uptime in milliseconds.
      Returns:
      the uptime in ms
    • getEstablishedOn

      public long getEstablishedOn()
      Time this connection was established.
      Returns:
      established timestamp, or 0 if not yet established
    • getMessagesSent

      public int getMessagesSent()
      Messages sent on this connection.
      Returns:
      how many messages have been written on this connection
    • getMessagesReceived

      public int getMessagesReceived()
      Messages received on this connection.
      Returns:
      how many messages have been read on this connection
    • getOutboundQueueSize

      public int getOutboundQueueSize()
      Return the number of messages waiting to be sent.
      Returns:
      outbound queue size
    • hasCurrentOutbound

      private boolean hasCurrentOutbound()
      Check if there are pending outbound messages.
      Returns:
      true if a message is still held for the current outbound write
    • drainOutboundTo

      private int drainOutboundTo(Queue<OutNetMessage> to)
      Drain any pending outbound messages to a new queue.
      Parameters:
      to - the queue the pending messages are moved into
      Returns:
      how many pending messages were moved into the given queue
    • getTimeSinceSend

      public long getTimeSinceSend()
      Time since last send in milliseconds.
      Returns:
      time since last send in ms
    • getTimeSinceSend

      public long getTimeSinceSend(long now)
      Time since last send in milliseconds.
      Parameters:
      now - the current time in milliseconds since the epoch
      Returns:
      how many milliseconds have passed since the last send
    • getTimeSinceReceive

      public long getTimeSinceReceive()
      Time since last receive in milliseconds.
      Returns:
      time since last receive in ms
    • getTimeSinceReceive

      public long getTimeSinceReceive(long now)
      Time since last receive in milliseconds.
      Parameters:
      now - the current time in milliseconds since the epoch
      Returns:
      how many milliseconds have passed since the last receive
    • getTimeSinceCreated

      public long getTimeSinceCreated()
      Time since connection creation in milliseconds.
      Returns:
      time since creation in ms
    • getEstimatedRTT

      public int getEstimatedRTT()
      Round trip time of the handshake that established this session.

      Recorded once when establishment completes, from the same now - getCreated() interval the clock-skew calculation already performs, and not refreshed for the life of the session. The session's current latency is a question for the transport's own estimator; this is the cost of getting the link up, which is what a profile's first-hop RTT is recorded as.

      Returns:
      the handshake round trip time in ms, or 0 if not yet established
    • noteEstablishmentRtt

      void noteEstablishmentRtt(int rttMs)
      Record the handshake round trip time, once the establishment completes.

      First writer wins. Both establishment directions converge here, so whichever observes VERIFIED first supplies the sample and a late second observation cannot overwrite it - the value has exactly one source per session, and re-deriving it later would make the same session's latency depend on read timing.

      Parameters:
      rttMs - measured round trip time in ms; values of 0 or less are ignored
    • getEstimatedRTTTime

      public long getEstimatedRTTTime()
      When getEstimatedRTT() was measured, on the router clock.
      Returns:
      the handshake completion time in ms, or Transport.RTT_TIME_UNKNOWN before establishment has supplied a reading
    • shouldRecordEstablishmentRtt

      static boolean shouldRecordEstablishmentRtt(int existingRttMs, long candidateRttMs)
      Whether a candidate handshake timing should become this session's recorded latency.

      Extracted as a pure predicate so the rule is testable without a router context and a live socket. Two rejections, both about not inventing a measurement: a non-positive interval means the clock did not advance, and an already-recorded value means this session's latency already has a source.

      Parameters:
      existingRttMs - the value already recorded, 0 if none
      candidateRttMs - the newly measured interval
      Returns:
      true if the candidate should be recorded
    • getTimeSinceCreated

      public long getTimeSinceCreated(long now)
      Time since connection creation in milliseconds.
      Parameters:
      now - the current time in milliseconds since the epoch
      Returns:
      how many milliseconds have passed since the connection was created
    • getCreated

      public long getCreated()
      When this connection was created (not established).
      Returns:
      the creation timestamp
    • getVersion

      public int getVersion()
      The NTCP2 version, for the console. For outbound, will not change. For inbound, defaults to 1, may change to 2 after establishment.
      Returns:
      the version, 1 or 2
    • setVersion

      public void setVersion(int ver)
      Records version 2 from InboundEstablishState. Just for logging, so we know before finishInboundEstablish() is called.
      Parameters:
      ver - the version to set
    • setMayDisconnect

      public void setMayDisconnect()
      The may-disconnect flag, set to true.
    • getMayDisconnect

      public boolean getMayDisconnect()
      Whether the connection may be disconnected.
      Returns:
      true if may disconnect
    • clearZeroRead

      public void clearZeroRead()
      Reset the zero-read counters the EventPumper uses to spot a stalled peer.
    • gotZeroRead

      public int gotZeroRead()
      Track time of zero-reads to avoid closing healthy connections.
      Returns:
      current count after incrementing
    • getLastZeroReadTime

      public long getLastZeroReadTime()
      Time of the last zero-byte read.
      Returns:
      last zero read time
    • isClosed

      public boolean isClosed()
      Whether the connection is closed.
      Returns:
      true if closed
    • close

      public void close()
      Specified by:
      close in interface AutoCloseable
      Specified by:
      close in interface Closeable
    • close

      public void close(boolean allowRequeue)
      Close the connection, dropping or requeueing whatever is still pending.
      Parameters:
      allowRequeue - if true, requeue pending messages for another tunnel
    • closeOnTimeout

      void closeOnTimeout(String cause, Exception e)
      Close and release EstablishState resources.
      Parameters:
      cause - the cause of the timeout
      e - may be null
    • locked_close

      private NTCPConnection locked_close(boolean allowRequeue)
      The connection; usually this, but could be a second connection with the same peer.
      Parameters:
      allowRequeue - whether to hand the unsent messages back for requeueing
      Returns:
      usually this, but could be a second connection with the same peer... only this or null as of 0.9.37
    • send

      public void send(OutNetMessage msg)
      Queue the message onto the connection's send queue.
      Parameters:
      msg - the message to send
    • isBacklogged

      public boolean isBacklogged()
      Whether the outbound queue is backlogged.
      Returns:
      true if outbound queue is backlogged
    • tooBacklogged

      public boolean tooBacklogged()
      Returns true if the outbound connection is currently too backlogged to accept new messages, allowing a short grace period after connection establishment and optionally logging details.
      Returns:
      true if too backlogged
    • enqueueInfoMessage

      void enqueueInfoMessage()
      Inject a DatabaseStoreMessage with our RouterInfo. NTCP 1 or 2.

      Externally, this is only called by NTCPTransport for outbound cons, before the con is established, but we know what version it is.

      Internally, may be called for outbound or inbound, but only after the con is established, so we know what the version is.

    • prepareNextWrite

      void prepareNextWrite(NTCPConnection.PrepBuffer prep)
      Prepare the next I2NP message for transmission. This should be run from the Writer thread pool. NTCP 1 or 2.

      This is the entry point as called from Writer.Runner.run().

      Parameters:
      prep - an instance of PrepBuffer to use as scratch space
    • getMessageDataSize

      static int getMessageDataSize(I2NPMessage m)
      NTCP2 frame-size contribution of one message.

      The NTCP2 block header (3 bytes, see NTCP2Payload.BLOCK_HEADER_SIZE) replaces NTCP1's 16-byte I2NP framing, so a message adds roughly 7 bytes less than its serialized size to a frame. The exact figure mirrors the established packing arithmetic in prepareNextWriteNTCP2(NTCPConnection.PrepBuffer) so frame sizes are unchanged.

      Parameters:
      m - the message being packed into the frame
      Returns:
      the message's estimated contribution in bytes
    • canPackMoreMessages

      static boolean canPackMoreMessages(int size, int messageDataSize)
      Whether a secondary message fits within the preferred NTCP2 payload cap.

      The cap keeps frames near the preferred 5 KB working size instead of filling the whole 16 KB buffer, so a trailing message is added only while the accumulated frame size (MAC plus already-packed blocks) stays under NTCP2_PREFERRED_PAYLOAD_MAX.

      Parameters:
      size - current frame size (MAC + packed blocks)
      messageDataSize - the candidate message's contribution
      Returns:
      true if adding the message keeps the frame at or below the cap
    • canSendDatetimeBlock

      static boolean canSendDatetimeBlock(int size, long nextMetaTime, long now)
      Whether an unsolicited datetime block may be packed into the frame.

      Scheduled by _nextMetaTime; the block is a 2-byte type, 2-byte length and 4-byte datetime, and is only added while the frame still fits in the full 16 KB buffer after its 3-byte block header.

      Parameters:
      size - current frame size (MAC + packed blocks)
      nextMetaTime - when the next datetime block is due, Long.MAX_VALUE until the connection is established
      now - current router time
      Returns:
      true if a datetime block is due and fits
    • canSendRouterInfoBlock

      static boolean canSendRouterInfoBlock(int size, long nextInfoTime, long now)
      Whether an unsolicited RouterInfo block may be attempted.

      Scheduled by _nextInfoTime; the heuristic estimate of 1024 bytes is only a first gate - the block is discarded and retried later if its real size does not fit (see canFitRouterInfoBlock(int, int)).

      Parameters:
      size - current frame size (MAC + packed blocks)
      nextInfoTime - when the next RouterInfo block is due, Long.MAX_VALUE until the connection is established
      now - current router time
      Returns:
      true if a RouterInfo block is due and plausibly fits
    • canFitRouterInfoBlock

      static boolean canFitRouterInfoBlock(int size, int infoSize)
      Whether the serialized RouterInfo block actually fits in the frame.

      This is the authoritative check after the 1024-byte estimate in canSendRouterInfoBlock(int, long, long).

      Parameters:
      size - current frame size (MAC + packed blocks)
      infoSize - the sized RouterInfo block's total length
      Returns:
      true if the block fits within the 16 KB buffer
    • isValidFrameLength

      static boolean isValidFrameLength(int framelen)
      Whether a received NTCP2 frame length is sane.

      A frame always carries a 16-byte MAC at minimum; anything shorter is definitely corrupt and the connection is torn down rather than buffered.

      Parameters:
      framelen - the length announced in the frame header
      Returns:
      true if the frame is long enough to hold its MAC
    • framePayloadLength

      static int framePayloadLength(int framelen)
      The payload length of a data-phase frame: the announced frame length minus the trailing 16-byte AEAD MAC.
      Parameters:
      framelen - the full frame length, including the MAC
      Returns:
      the number of bytes to pass to NTCP2Payload.processPayload(I2PAppContext, NTCP2Payload.PayloadCallback, byte[], int, int, boolean)
    • isTooBacklogged

      static boolean isTooBacklogged(long uptimeMs, boolean outboundBacklogged)
      Whether the outbound queue is too backlogged to accept new messages.

      There is a 15 second grace period right after connection establishment so messages queued for a brand-new connection are not refused, then the decision is delegated to the bandwidth limiter's backlog flag.

      Parameters:
      uptimeMs - the connection uptime, in milliseconds
      outboundBacklogged - the bandwidth limiter's backlog flag for the outbound queue
      Returns:
      true if the queue is too backlogged
    • isSamePeer

      static boolean isSamePeer(Hash h, Hash peerHash)
      Whether a received RouterInfo's hash identifies the peer we are talking to.
      Parameters:
      h - the hash of the received RouterInfo
      peerHash - the hash of the remote peer of this connection
      Returns:
      true if the hash matches the remote peer
    • isOwnRouterInfo

      static boolean isOwnRouterInfo(Hash h, Hash ourHash)
      Whether a received RouterInfo's hash identifies us.
      Parameters:
      h - the hash of the received RouterInfo
      ourHash - our own router hash
      Returns:
      true if the hash is our own
    • isNewerOrNew

      static boolean isNewerOrNew(RouterInfo old, RouterInfo ri)
      Whether a RouterInfo we just stored is strictly newer than whatever was stored before (or there was no previous copy, so the store is new). copy claims a later publication time.

      Used on the flood path in NTCP2Payload.PayloadCallback.gotRI(RouterInfo, boolean, boolean).

      Parameters:
      old - the previously stored RouterInfo, or null if this is a new store
      ri - the RouterInfo that was just stored
      Returns:
      true if flooding is justified by freshness
    • canZeroCopyFrame

      static boolean canZeroCopyFrame(int received, int remaining, int framelen)
      Whether the whole frame can be decrypted in place, bypassing the scratch buffer.

      Requires being at a frame boundary (received == 0) with the full announced frame already present in the pumper's buffer, so the ciphertext can be overwritten in place zero-copy.

      Parameters:
      received - bytes of the current frame read so far outside the shortcut
      remaining - bytes left in the pumper buffer
      framelen - the announced frame length
      Returns:
      true when the shortcut applies
    • needsFrameBuffer

      static boolean needsFrameBuffer(ByteArray dataBuf, int received, int framelen)
      Whether the buffered (split-frame) read path needs a fresh destination buffer for the announced frame.

      Only at a frame boundary with no existing buffer large enough does the read state allocate. Otherwise the partially filled buffer is reused.

      Parameters:
      dataBuf - the current scratch buffer, or null
      received - bytes of the current frame read so far outside the shortcut
      framelen - the announced frame length
      Returns:
      true if a new buffer must be allocated
    • prepareNextWriteNTCP2

      private void prepareNextWriteNTCP2(NTCPConnection.PrepBuffer buf)
      Prepare the next I2NP message for transmission. This should be run from the Writer thread pool.

      Caller must synchronize on _writeLock.

      Parameters:
      buf - we use buf.enencrypted only
    • getPaddingSize

      private int getPaddingSize(int dataSize, int availForPad)
      NTCP2 only.
      Parameters:
      dataSize - the total size of the data we are sending
      availForPad - the available size for padding, not including padding block header, must be greater than zero
      Returns:
      min 0 max availForPad
    • sendOurRouterInfo

      void sendOurRouterInfo(boolean shouldFlood)
      NTCP2 only.
      Parameters:
      shouldFlood - whether to flood the router info
    • sendRouterInfo

      private void sendRouterInfo(RouterInfo ri, boolean shouldFlood)
      NTCP2 only.
      Parameters:
      ri - the router info to send to this peer
      shouldFlood - whether to flood the router info
    • sendTerminationAndClose

      void sendTerminationAndClose()
      NTCP2. For NTCP2, sends termination and then closes the connection after a brief delay.
    • sendTermination

      private void sendTermination(int reason, int validFramesRcvd)
      NTCP2 only. Also closes the connection after a brief delay.
      Parameters:
      reason - the termination reason code, one of the REASON_* constants
      validFramesRcvd - how many valid frames were received, reported to the peer
    • sendNTCP2

      private void sendNTCP2(byte[] tmp, List<NTCP2Payload.Block> blocks)
      This constructs the payload from the blocks, using the tmp byte array, then encrypts the payload and passes it to the pumper for writing.

      The frame bytes go into a fixed-size pooled buffer (see EventPumper.acquireWriteBuf()) instead of a per-frame allocation: frames are variable-length, so the old new byte[2 + framelen] rarely matched the pool's size class and was reallocated on every send. Only the used 2 + framelen prefix is handed to the writer; the pumper returns the buffer to the pool after the frame has fully drained.

      Parameters:
      tmp - the output buffer, must have room for the blocks written by NTCP2Payload.writePayload(byte[], int, List); may be released immediately on return
      blocks - the blocks to write into tmp
    • outboundConnected

      void outboundConnected()
      Async callback after the outbound connection was completed (this should NOT block, as it occurs in the selector thread).
    • removeIBRequest

      private boolean removeIBRequest(FIFOBandwidthLimiter.Request req)
      Hands off ownership of an in-flight read bandwidth request back to the caller. Exactly one caller - the limiter thread when the request is fully allocated, or the closing thread via abort() -> notifyAllocation() - receives true and thereby owns the request's attached read buffer; false callers must leave the buffer alone. This membership check is what makes the read-back pressure release path exactly-once without a lock.
      Parameters:
      req - the request to hand off
      Returns:
      true if this caller won the hand-off and owns the attached buffer
    • addIBRequest

      private void addIBRequest(FIFOBandwidthLimiter.Request req)
      Tracks an inbound bandwidth request.
      Parameters:
      req - the request to track
    • removeOBRequest

      private void removeOBRequest(FIFOBandwidthLimiter.Request req)
      Stops tracking an outbound bandwidth request.
      Parameters:
      req - the request to forget
    • addOBRequest

      private void addOBRequest(FIFOBandwidthLimiter.Request req)
      Tracks an outbound bandwidth request.
      Parameters:
      req - the request to track
    • wantsWrite

      void wantsWrite(byte[] data)
      Call when there is data ready to write. If we have bandwidth, calls write() which calls EventPumnper.wantsWrite(con). If no bandwidth, calls queuedWrite(ByteBuffer, FIFOBandwidthLimiter.Request).
      Parameters:
      data - the data to write
    • wantsWrite

      void wantsWrite(byte[] data, int off, int len)
      Call when there is data ready to write. If we have bandwidth, calls write() which calls EventPumnper.wantsWrite(con). If no bandwidth, calls queuedWrite().
      Parameters:
      data - the data to write
      off - the offset in the data
      len - the length of the data
    • queuedRecv

      void queuedRecv(ByteBuffer buf, FIFOBandwidthLimiter.Request req)
      We have read the data in the buffer, but we can't process it locally yet, because we're choked by the bandwidth limiter. Cache the contents of the buffer (not copy) and register ourselves to be notified when the contents have been fully allocated The request joins the in-flight set before the listener is registered, so the listener's ownership hand-off always has a member to claim - including when the request is already satisfied and setCompleteListener() completes synchronously.
      Parameters:
      buf - the buffer to queue
      req - the bandwidth request
    • queuedWrite

      private void queuedWrite(ByteBuffer buf, FIFOBandwidthLimiter.Request req)
      Queue a buffer for writing once its bandwidth has been allocated, the outbound counterpart of queuedRecv(ByteBuffer, FIFOBandwidthLimiter.Request).
      Parameters:
      buf - the buffer to queue
      req - the bandwidth request that re-triggers the write once satisfied
    • recv

      void recv(ByteBuffer buf)
      The contents of the buffer have been read and can be processed asap. This should not block, and the NTCP connection now owns the buffer to do with as it pleases BUT it should eventually copy out the data and call EventPumper.releaseBuf(ByteBuffer).
      Parameters:
      buf - the buffer received
    • shouldPauseReads

      static boolean shouldPauseReads(boolean readPaused, int queuedReads)
      Decide whether socket reads should stop because the queue of unread buffers has reached the limit. Once paused, reads stay paused until the queue drains, so a peer cannot push us back over the limit repeatedly.
      Parameters:
      readPaused - whether reads are already paused
      queuedReads - buffers waiting to be processed
      Returns:
      true if the pause should be entered
    • isReadQueueFull

      boolean isReadQueueFull()
      Whether the read queue is full and the socket should stop being read until the queue has drained.
      Returns:
      true if reads are paused
    • write

      private void write(ByteBuffer buf)
      The contents of the buffer have been encrypted / padded / etc and have been fully allocated for the bandwidth limiter.

      Only enqueues the buffer and registers write interest. The socket write itself is performed exclusively by the pumper thread in EventPumper.processWrite(SelectionKey): this single-writer contract guarantees that at most one thread calls SocketChannel.write(ByteBuffer) on this connection at a time, so frame bytes can never interleave. It also removes the need to hold _writeLock across the socket drain.

      Parameters:
      buf - the fully allocated buffer to write out
    • getNextReadBuf

      ByteBuffer getNextReadBuf()
      Next read buffer, or null if none available.
      Returns:
      the next read buffer, or null
    • shouldResumeReads

      static boolean shouldResumeReads(boolean readPaused, boolean queueEmpty)
      Decide whether socket reads should start again after the read queue paused them. Only the empty queue ends the pause, so a slow reader keeps applying backpressure until it has caught up.
      Parameters:
      readPaused - whether reads are currently paused
      queueEmpty - whether the queue came up empty
      Returns:
      true if the pause should be lifted
    • isWriteBufEmpty

      boolean isWriteBufEmpty()
      Replaces getWriteBufCount(). Lock-free; backed by ConcurrentLinkedQueue, and only the pumper's drain loop consumes. No synchronization required.
      Returns:
      true if write buffer is empty
    • getNextWriteBuf

      ByteBuffer getNextWriteBuf()
      Returns but does not remove the buffer. Call removeWriteBuf() after write complete. Lock-free; backed by ConcurrentLinkedQueue, and only the pumper's drain loop consumes. No synchronization required.
      Returns:
      null if none available
    • removeWriteBuf

      void removeWriteBuf(ByteBuffer buf)
      Remove the buffer, which must be the current head of _writeBufs. Called only from the pumper's drain loop (EventPumper.processWrite(SelectionKey)). Removal is O(1) via poll(): the single-writer drain guarantees the buffer is at the head (producers only add, the pumper is the only consumer), replacing the previous O(n) equals()-scan. The completion bookkeeping self-synchronizes on _writeLock; the _writeBufs queue operations are lock-free.
      Parameters:
      buf - the buffer to remove
    • getSendRate

      public float getSendRate()
      Current send rate in Bps.
      Returns:
      the rate, in bytes per second
    • getRecvRate

      public float getRecvRate()
      Current receive rate in Bps.
      Returns:
      the rate, in bytes per second
    • updateStats

      private void updateStats()
      Stats only for console.
    • recvEncryptedI2NP

      void recvEncryptedI2NP(ByteBuffer buf)
      Connection must be established!

      The contents of the buffer include some fraction of one or more encrypted and encoded I2NP messages. Individual I2NP messages are encoded as "sizeof(data)+data+pad+crc", and those are encrypted with the session key and the last 16 bytes of the previous encrypted I2NP message.

      The NTCP connection now owns the buffer BUT it must copy out the data as reader will call EventPumper.releaseBuf(ByteBuffer).

      This is the entry point as called from Reader.processRead(NTCPConnection).

      Parameters:
      buf - the buffer containing encrypted data
    • receiveTimestamp

      private void receiveTimestamp(long ts)
      Handle a received timestamp, NTCP 1 or 2. Caller must synch.
      Parameters:
      ts - his timestamp in seconds, NOT ms
    • acquireReadBuf

      private static ByteArray acquireReadBuf()
      Takes a read buffer from the cache.
      Returns:
      the buffer, zeroed
    • releaseReadBuf

      private static void releaseReadBuf(ByteArray buf)
      Returns a read buffer to the cache.
      Parameters:
      buf - the buffer to return
    • banCheckIsDue

      static boolean banCheckIsDue(long now, long nextCheckAt)
      Whether the cached ban/blocklist verdict has to be re-read.

      Pure and static so the boundary can be pinned without a socket: the deadline is inclusive, so a verdict cached at T is re-read at T + interval and not before.

      Parameters:
      now - current clock time in ms
      nextCheckAt - the time the cached verdict stops being valid, in ms; 0 means nothing is cached yet
      Returns:
      true if the lists must be read again
    • isBanned

      public boolean isBanned()
      Check if the peer is blocklisted or banned. If so, log and close the connection.

      The answer is cached for BAN_RECHECK_MS, so a caller on a frame loop does not pay for the lookup per frame. The first call on a connection is never served from the cache, so establishment still rejects a banned peer immediately.

      Returns:
      true if banned or blocklisted, false otherwise
    • finishOutboundEstablishment

      void finishOutboundEstablishment(CipherState sender, CipherState receiver, byte[] sip_ab, byte[] sip_ba, long clockSkew)
      We are Alice. NTCP2 only.

      Caller MUST call recvEncryptedI2NP() after, for any remaining bytes in receive buffer.

      Parameters:
      sender - use to send to Bob
      receiver - use to receive from Bob
      sip_ab - 24 bytes to init SipHash to Bob
      sip_ba - 24 bytes to init SipHash from Bob
      clockSkew - OUR clock minus BOB's clock in seconds (may be negative, obviously, but |val| should be under 1 minute)
    • finishInboundEstablishment

      void finishInboundEstablishment(CipherState sender, CipherState receiver, byte[] sip_ba, byte[] sip_ab, long clockSkew, NTCP2Options hisPadding)
      We are Bob. NTCP2 only.

      Caller MUST call recvEncryptedI2NP() after, for any remaining bytes in receive buffer.

      Parameters:
      sender - use to send to Alice
      receiver - use to receive from Alice
      sip_ba - 24 bytes to init SipHash to Alice
      sip_ab - 24 bytes to init SipHash from Alice
      clockSkew - OUR clock minus ALICE's clock in seconds (may be negative, obviously, but |val| should be under 1 minute)
      hisPadding - may be null
    • failInboundEstablishment

      void failInboundEstablishment(CipherState sender, byte[] sip_ba, int reason)
      We are Bob. NTCP2 only. This is only for invalid payload received in message 3. We send a termination and close. There will be no receiving.
      Parameters:
      sender - use to send to Alice
      sip_ba - 24 bytes to init SipHash to Alice
      reason - the reason code
    • finishEstablishment

      private void finishEstablishment(CipherState sender, CipherState receiver, byte[] sip_send, byte[] sip_recv, long clockSkew)
      We are Alice or Bob. NTCP2 only.
      Parameters:
      sender - use to send
      receiver - use to receive
      sip_send - 24 bytes to init SipHash out
      sip_recv - 24 bytes to init SipHash in
      clockSkew - the clock skew in seconds; a positive value means our clock is ahead
    • delayedClose

      private void delayedClose(ByteBuffer buf, int validFramesRcvd)
      After an AEAD failure, read a random number of bytes, with a brief timeout, and then fail. This replaces _curReadState, so no more messages will be received.

      Call this only on data phase AEAD failure. For other failures, use sendTermination(int, int).

      Parameters:
      buf - possibly with data remaining
      validFramesRcvd - to be sent in termination message
    • fromLong8LE

      private static long fromLong8LE(byte[] src, int offset)
      Little endian. Same as DataHelper.fromLongLE(byte[], int, int) called with 8 bytes, but allows a negative result.
      Parameters:
      src - the array to read 8 bytes from
      offset - the index of the first byte to read
      Returns:
      the 8 bytes read little-endian, as a signed long
      Throws:
      ArrayIndexOutOfBoundsException - if offset is not inside the array
    • toLong8LE

      private static void toLong8LE(byte[] target, int offset, long value)
      Little endian. Same as DataHelper.toLongLE(byte[], int, int, long) called with 8 bytes, but allows a negative value.
      Parameters:
      target - the array to write 8 bytes into
      offset - the index of the first byte to write
      value - the value to write, low byte first
    • hasWriteInterestPending

      public boolean hasWriteInterestPending()
      Whether the pumper has registered write interest.
      Returns:
      true if write interest is pending
    • compareAndSetWriteInterestPending

      public boolean compareAndSetWriteInterestPending(boolean expected, boolean update)
      Atomically updates the write-interest-pending flag.
      Parameters:
      expected - the flag value currently stored, compared against for equality
      update - the flag value to store if the comparison succeeds
      Returns:
      true if CAS succeeded
    • updateLastActiveTime

      public void updateLastActiveTime()
      Update last active time to now.
    • getLastActiveTime

      public long getLastActiveTime()
      Time of the last activity.
      Returns:
      last active time
    • shouldSetInterest

      public boolean shouldSetInterest(int ops)
      Check if the given interest op is not already set.
      Parameters:
      ops - the interest ops to check
      Returns:
      true if the op is not already set
    • updateInterestOps

      public void updateInterestOps(int ops)
      Update the cached interest ops.
      Parameters:
      ops - the new interest ops
    • toShortString

      private String toShortString()
      Compact one-line connection identifier for log output: direction, remote address, connection id and peer hash. No stats, no newlines.
      Returns:
      the identifier, never null
    • toString

      public String toString()
      Overrides:
      toString in class Object