From 1e89fac192eafb3935d67333f0339704f6d4afde Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Wed, 18 Nov 2015 18:12:23 +0000 Subject: [PATCH] SSU: Add support for requesting a relay tag via Session Request extended options (ticket #1465) --- .../transport/udp/EstablishmentManager.java | 13 +++++-- .../transport/udp/InboundEstablishState.java | 12 +++++++ .../transport/udp/OutboundEstablishState.java | 18 +++++++++- .../router/transport/udp/PacketBuilder.java | 19 ++++++---- .../i2p/router/transport/udp/UDPPacket.java | 8 +++++ .../router/transport/udp/UDPPacketReader.java | 35 ++++++++++++++----- 6 files changed, 86 insertions(+), 19 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index 53e7e9d8dd..5ec58a9e9c 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -131,8 +131,11 @@ class EstablishmentManager { * Java I2P has always parsed the length of the extended options field, * but i2pd hasn't recognized it until this release. * No matter, the options weren't defined until this release anyway. + * + * FIXME 0.9.22 for testing, change to 0.9.24 for release + * */ - private static final String VERSION_ALLOW_EXTENDED_OPTIONS = "0.9.24"; + private static final String VERSION_ALLOW_EXTENDED_OPTIONS = "0.9.22"; public EstablishmentManager(RouterContext ctx, UDPTransport transport) { @@ -367,8 +370,11 @@ class EstablishmentManager { } boolean allowExtendedOptions = VersionComparator.comp(toRouterInfo.getVersion(), VERSION_ALLOW_EXTENDED_OPTIONS) >= 0; + // w/o ext options, it's always 'requested', no need to set + boolean requestIntroduction = allowExtendedOptions && _transport.introducersRequired(); state = new OutboundEstablishState(_context, maybeTo, to, toIdentity, allowExtendedOptions, + requestIntroduction, sessionKey, addr, _transport.getDHFactory()); OutboundEstablishState oldState = _outboundStates.putIfAbsent(to, state); boolean isNew = oldState == null; @@ -488,8 +494,9 @@ class EstablishmentManager { // Don't offer to relay to privileged ports. // Only offer for an IPv4 session. // TODO if already we have their RI, only offer if they need it (no 'C' cap) - // TODO if extended options, only if they asked for it - if (_transport.canIntroduce() && state.getSentPort() >= 1024 && + // if extended options, only if they asked for it + if (state.isIntroductionRequested() && + _transport.canIntroduce() && state.getSentPort() >= 1024 && state.getSentIP().length == 4) { // ensure > 0 long tag = 1 + _context.random().nextLong(MAX_TAG_VALUE); diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java index 590aa29ed2..96d45c216b 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java @@ -62,6 +62,8 @@ class InboundEstablishState { private final Queue<OutNetMessage> _queuedMessages; // count for backoff private int _createdSentCount; + // default true + private boolean _introductionRequested = true; public enum InboundState { /** nothin known yet */ @@ -150,6 +152,10 @@ class InboundEstablishState { if (_bobIP == null) _bobIP = new byte[req.readIPSize()]; req.readIP(_bobIP, 0); + byte[] ext = req.readExtendedOptions(); + if (ext != null && ext.length >= UDPPacket.SESS_REQ_MIN_EXT_OPTIONS_LENGTH) { + _introductionRequested = (ext[1] & (byte) UDPPacket.SESS_REQ_EXT_FLAG_REQUEST_RELAY_TAG) != 0; + } if (_log.shouldLog(Log.DEBUG)) _log.debug("Receive sessionRequest, BobIP = " + Addresses.toString(_bobIP)); if (_currentState == InboundState.IB_STATE_UNKNOWN) @@ -160,6 +166,12 @@ class InboundEstablishState { public synchronized boolean sessionRequestReceived() { return _receivedX != null; } public synchronized byte[] getReceivedX() { return _receivedX; } public synchronized byte[] getReceivedOurIP() { return _bobIP; } + /** + * True (default) if no extended options in session request, + * or value of flag bit in the extended options. + * @since 0.9.24 + */ + public synchronized boolean isIntroductionRequested() { return _introductionRequested; } /** * Generates session key and mac key. diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java index cfb0d2f577..d849bb836b 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java @@ -57,6 +57,7 @@ class OutboundEstablishState { private final RemoteHostId _claimedAddress; private final RouterIdentity _remotePeer; private final boolean _allowExtendedOptions; + private final boolean _needIntroduction; private final SessionKey _introKey; private final Queue<OutNetMessage> _queuedMessages; private OutboundState _currentState; @@ -108,12 +109,16 @@ class OutboundEstablishState { * @param claimedAddress an IP/port based RemoteHostId, or null if unknown * @param remoteHostId non-null, == claimedAddress if direct, or a hash-based one if indirect * @param remotePeer must have supported sig type + * @param allowExtenededOptions are we allowed to send extended options to Bob? + * @param needIntroduction should we ask Bob to be an introducer for us? + ignored unless allowExtendedOptions is true * @param introKey Bob's introduction key, as published in the netdb * @param addr non-null */ public OutboundEstablishState(RouterContext ctx, RemoteHostId claimedAddress, RemoteHostId remoteHostId, RouterIdentity remotePeer, boolean allowExtendedOptions, + boolean needIntroduction, SessionKey introKey, UDPAddress addr, DHSessionKeyBuilder.Factory dh) { _context = ctx; @@ -128,6 +133,7 @@ class OutboundEstablishState { _claimedAddress = claimedAddress; _remoteHostId = remoteHostId; _allowExtendedOptions = allowExtendedOptions; + _needIntroduction = needIntroduction; _remotePeer = remotePeer; _introKey = introKey; _queuedMessages = new LinkedBlockingQueue<OutNetMessage>(); @@ -161,8 +167,18 @@ class OutboundEstablishState { /** @return -1 if unset */ public long getIntroNonce() { return _introductionNonce; } - /** @since 0.9.24 */ + /** + * Are we allowed to send extended options to this peer? + * @since 0.9.24 + */ public boolean isExtendedOptionsAllowed() { return _allowExtendedOptions; } + + /** + * Should we ask this peer to be an introducer for us? + * Ignored unless allowExtendedOptions is true + * @since 0.9.24 + */ + public boolean needIntroduction() { return _needIntroduction; } /** * Queue a message to be sent after the session is established. diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java index 594388716b..a777d819b7 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java @@ -783,15 +783,20 @@ class PacketBuilder { * @return ready to send packet, or null if there was a problem */ public UDPPacket buildSessionRequestPacket(OutboundEstablishState state) { - // TODO - // boolean ext = state.isExtendedOptionsAllowed(); - // if (ext) - //byte[] options = new byte[3]; - //UDPPacket packet = buildPacketHeader(SESSION_REQUEST_FLAG_BYTE, options); - UDPPacket packet = buildPacketHeader(SESSION_REQUEST_FLAG_BYTE); + int off = HEADER_SIZE; + byte[] options; + boolean ext = state.isExtendedOptionsAllowed(); + if (ext) { + options = new byte[UDPPacket.SESS_REQ_MIN_EXT_OPTIONS_LENGTH]; + if (state.needIntroduction()) + options[1] = (byte) UDPPacket.SESS_REQ_EXT_FLAG_REQUEST_RELAY_TAG; + off += UDPPacket.SESS_REQ_MIN_EXT_OPTIONS_LENGTH + 1; + } else { + options = null; + } + UDPPacket packet = buildPacketHeader(SESSION_REQUEST_FLAG_BYTE, options); DatagramPacket pkt = packet.getPacket(); byte data[] = pkt.getData(); - int off = HEADER_SIZE; // + 1 + options.length; byte toIP[] = state.getSentIP(); if (!_transport.isValid(toIP)) { diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java index fae13ab1ff..a68b848201 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java @@ -101,6 +101,14 @@ class UDPPacket implements CDQEntry { */ public static final byte HEADER_FLAG_EXTENDED_OPTIONS = (1 << 2); + // Extended options for session request + public static final int SESS_REQ_MIN_EXT_OPTIONS_LENGTH = 2; + // bytes 0-1 are flags + /** + * set to 1 to request a session tag, i.e. we want him to be an introducer for us + */ + public static final int SESS_REQ_EXT_FLAG_REQUEST_RELAY_TAG = 0x01; + // various flag fields for use in the data packets public static final byte DATA_FLAG_EXPLICIT_ACK = (byte)(1 << 7); public static final byte DATA_FLAG_ACK_BITFIELDS = (1 << 6); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java index 34f8d7bf7b..616f48c4ae 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java @@ -104,6 +104,7 @@ class UDPPacketReader { /** * Returns extended option data, 0-255 bytes, or null if none. + * Returned array does NOT include the length byte. * * @return extended options or null if none is included * @since 0.9.24 @@ -175,8 +176,26 @@ class UDPPacketReader { /* ------- Begin Reader Classes ------- */ + /** + * Base + * + * @since 0.9.24 + */ + public abstract class Reader { + /** + * Returns extended option data from the header, 0-255 bytes, or null if none. + * Returned array does NOT include the length byte. + * + * @return extended options or null if none is included + * @since 0.9.24 + */ + public byte[] readExtendedOptions() { + return UDPPacketReader.this.readExtendedOptions(); + } + } + /** Help read the SessionRequest payload */ - public class SessionRequestReader { + public class SessionRequestReader extends Reader { public static final int X_LENGTH = 256; public void readX(byte target[], int targetOffset) { int readOffset = readBodyOffset(); @@ -198,7 +217,7 @@ class UDPPacketReader { } /** Help read the SessionCreated payload */ - public class SessionCreatedReader { + public class SessionCreatedReader extends Reader { public static final int Y_LENGTH = 256; public void readY(byte target[], int targetOffset) { int readOffset = readBodyOffset(); @@ -253,7 +272,7 @@ class UDPPacketReader { } /** parse out the confirmed message */ - public class SessionConfirmedReader { + public class SessionConfirmedReader extends Reader { /** which fragment is this? */ public int readCurrentFragmentNum() { int readOffset = readBodyOffset(); @@ -306,7 +325,7 @@ class UDPPacketReader { } /** parse out the data message */ - public class DataReader { + public class DataReader extends Reader { /** * @return the data size, NOT including IP header, UDP header, IV, or MAC @@ -642,7 +661,7 @@ class UDPPacketReader { } /** Help read the PeerTest payload */ - public class PeerTestReader { + public class PeerTestReader extends Reader { private static final int NONCE_LENGTH = 4; public long readNonce() { @@ -683,7 +702,7 @@ class UDPPacketReader { } /** Help read the RelayRequest payload */ - public class RelayRequestReader { + public class RelayRequestReader extends Reader { public long readTag() { long rv = DataHelper.fromLong(_message, readBodyOffset(), 4); if (_log.shouldLog(Log.DEBUG)) @@ -767,7 +786,7 @@ class UDPPacketReader { } /** Help read the RelayIntro payload */ - public class RelayIntroReader { + public class RelayIntroReader extends Reader { public int readIPSize() { int offset = readBodyOffset(); return _message[offset] & 0xff; @@ -808,7 +827,7 @@ class UDPPacketReader { /** Help read the RelayResponse payload */ - public class RelayResponseReader { + public class RelayResponseReader extends Reader { public int readCharlieIPSize() { int offset = readBodyOffset(); return _message[offset] & 0xff; -- GitLab