NTCP: Start NTCP2 implementation, all disabled for now

This commit is contained in:
zzz
2018-06-02 15:05:24 +00:00
parent 06106c75dc
commit 05ffa63dc0
8 changed files with 262 additions and 56 deletions

View File

@ -490,6 +490,23 @@ public class RouterInfo extends DatabaseEntry {
} }
return ret; return ret;
} }
/**
* For multiple addresses per-transport (IPv4 or IPv6)
* Return addresses matching either of two styles
*
* @return non-null
* @since 0.9.35
*/
public List<RouterAddress> getTargetAddresses(String transportStyle1, String transportStyle2) {
List<RouterAddress> ret = new ArrayList<RouterAddress>(_addresses.size());
for (RouterAddress addr : _addresses) {
String style = addr.getTransportStyle();
if (style.equals(transportStyle1) || style.equals(transportStyle2))
ret.add(addr);
}
return ret;
}
/** /**
* Actually validate the signature * Actually validate the signature

View File

@ -660,45 +660,54 @@ public abstract class TransportImpl implements Transport {
* @since IPv6 * @since IPv6
*/ */
protected List<RouterAddress> getTargetAddresses(RouterInfo target) { protected List<RouterAddress> getTargetAddresses(RouterInfo target) {
List<RouterAddress> rv = target.getTargetAddresses(getStyle()); List<RouterAddress> rv;
String alt = getAltStyle();
if (alt != null)
rv = target.getTargetAddresses(getStyle(), alt);
else
rv = target.getTargetAddresses(getStyle());
if (rv.isEmpty()) if (rv.isEmpty())
return rv; return rv;
// Shuffle so everybody doesn't use the first one if (rv.size() > 1) {
if (rv.size() > 1) // Shuffle so everybody doesn't use the first one
Collections.shuffle(rv, _context.random()); Collections.shuffle(rv, _context.random());
TransportUtil.IPv6Config config = getIPv6Config(); TransportUtil.IPv6Config config = getIPv6Config();
int adj; int adj;
switch (config) { switch (config) {
case IPV6_DISABLED: case IPV6_DISABLED:
adj = 10; adj = 10;
/**** IPv6 addresses will be rejected in isPubliclyRoutable() /**** IPv6 addresses will be rejected in isPubliclyRoutable()
for (Iterator<RouterAddress> iter = rv.iterator(); iter.hasNext(); ) { for (Iterator<RouterAddress> iter = rv.iterator(); iter.hasNext(); ) {
byte[] ip = iter.next().getIP(); byte[] ip = iter.next().getIP();
if (ip != null && ip.length == 16) if (ip != null && ip.length == 16)
iter.remove(); iter.remove();
} }
****/ ****/
break; break;
case IPV6_NOT_PREFERRED:
adj = 1; break; case IPV6_NOT_PREFERRED:
default: adj = 1; break;
case IPV6_ENABLED: default:
adj = 0; break;
case IPV6_PREFERRED: case IPV6_ENABLED:
adj = -1; break; adj = 0; break;
case IPV6_ONLY:
adj = -10; case IPV6_PREFERRED:
/**** IPv6 addresses will be rejected in isPubliclyRoutable() adj = -1; break;
for (Iterator<RouterAddress> iter = rv.iterator(); iter.hasNext(); ) {
byte[] ip = iter.next().getIP(); case IPV6_ONLY:
if (ip != null && ip.length == 4) adj = -10;
iter.remove(); /**** IPv6 addresses will be rejected in isPubliclyRoutable()
} for (Iterator<RouterAddress> iter = rv.iterator(); iter.hasNext(); ) {
****/ byte[] ip = iter.next().getIP();
break; if (ip != null && ip.length == 4)
} iter.remove();
if (rv.size() > 1) }
****/
break;
}
Collections.sort(rv, new AddrComparator(adj)); Collections.sort(rv, new AddrComparator(adj));
}
return rv; return rv;
} }
@ -976,6 +985,13 @@ public abstract class TransportImpl implements Transport {
} }
} }
/**
* An alternate supported style, or null.
* @return null, override to add support
* @since 0.9.35
*/
public String getAltStyle() { return null; }
/** /**
* @since 0.9.3 * @since 0.9.3
*/ */

View File

@ -226,6 +226,13 @@ abstract class EstablishBase implements EstablishState {
} }
} }
/**
* Get the NTCP version
* @return 1, 2, or 0 if unknown
* @since 0.9.35
*/
public abstract int getVersion();
/** Anything left over in the byte buffer after verification is extra /** Anything left over in the byte buffer after verification is extra
* *
* All data must be copied out of the buffer as Reader.processRead() * All data must be copied out of the buffer as Reader.processRead()
@ -337,12 +344,16 @@ abstract class EstablishBase implements EstablishState {
_state = State.VERIFIED; _state = State.VERIFIED;
} }
@Override public void prepareOutbound() { public int getVersion() { return 1; }
Log log =RouterContext.getCurrentContext().logManager().getLog(VerifiedEstablishState.class);
@Override
public void prepareOutbound() {
Log log = RouterContext.getCurrentContext().logManager().getLog(VerifiedEstablishState.class);
log.warn("prepareOutbound() on verified state, doing nothing!"); log.warn("prepareOutbound() on verified state, doing nothing!");
} }
@Override public String toString() { return "VerifiedEstablishState: ";} @Override
public String toString() { return "VerifiedEstablishState: ";}
} }
/** /**
@ -355,12 +366,16 @@ abstract class EstablishBase implements EstablishState {
_state = State.CORRUPT; _state = State.CORRUPT;
} }
@Override public void prepareOutbound() { public int getVersion() { return 1; }
Log log =RouterContext.getCurrentContext().logManager().getLog(VerifiedEstablishState.class);
@Override
public void prepareOutbound() {
Log log = RouterContext.getCurrentContext().logManager().getLog(VerifiedEstablishState.class);
log.warn("prepareOutbound() on verified state, doing nothing!"); log.warn("prepareOutbound() on verified state, doing nothing!");
} }
@Override public String toString() { return "FailedEstablishState: ";} @Override
public String toString() { return "FailedEstablishState: ";}
} }
/** /**

View File

@ -49,6 +49,13 @@ interface EstablishState {
*/ */
public byte[] getExtraBytes(); public byte[] getExtraBytes();
/**
* Get the NTCP version
* @return 1, 2, or 0 if unknown
* @since 0.9.35
*/
public int getVersion();
/** /**
* Release resources on timeout. * Release resources on timeout.
* @param e may be null * @param e may be null

View File

@ -21,7 +21,7 @@ import net.i2p.util.SimpleByteCache;
/** /**
* *
* We are Bob * NTCP 1 or 2. We are Bob.
* *
*/ */
class InboundEstablishState extends EstablishBase { class InboundEstablishState extends EstablishBase {
@ -69,6 +69,13 @@ class InboundEstablishState extends EstablishBase {
receiveInbound(src); receiveInbound(src);
} }
/**
* Get the NTCP version
* @return 1, 2, or 0 if unknown
* @since 0.9.35
*/
public int getVersion() { return 1; }
/** /**
* we are Bob, so receive these bytes as part of an inbound connection * we are Bob, so receive these bytes as part of an inbound connection
* This method receives messages 1 and 3, and sends messages 2 and 4. * This method receives messages 1 and 3, and sends messages 2 and 4.

View File

@ -201,8 +201,10 @@ public class NTCPConnection implements Closeable {
/** /**
* Create an outbound unconnected NTCP connection * Create an outbound unconnected NTCP connection
* *
* @param version must be 1 or 2
*/ */
public NTCPConnection(RouterContext ctx, NTCPTransport transport, RouterIdentity remotePeer, RouterAddress remAddr) { public NTCPConnection(RouterContext ctx, NTCPTransport transport, RouterIdentity remotePeer,
RouterAddress remAddr, int version) {
_context = ctx; _context = ctx;
_log = ctx.logManager().getLog(getClass()); _log = ctx.logManager().getLog(getClass());
_created = ctx.clock().now(); _created = ctx.clock().now();
@ -216,7 +218,10 @@ public class NTCPConnection implements Closeable {
//_outbound = new CoDelPriorityBlockingQueue(ctx, "NTCP-Connection", 32); //_outbound = new CoDelPriorityBlockingQueue(ctx, "NTCP-Connection", 32);
_outbound = new PriBlockingQueue<OutNetMessage>(ctx, "NTCP-Connection", 32); _outbound = new PriBlockingQueue<OutNetMessage>(ctx, "NTCP-Connection", 32);
_isInbound = false; _isInbound = false;
_establishState = new OutboundEstablishState(ctx, transport, this); //if (version == 1)
_establishState = new OutboundEstablishState(ctx, transport, this);
//else
// _establishState = // TODO
_decryptBlockBuf = new byte[BLOCK_SIZE]; _decryptBlockBuf = new byte[BLOCK_SIZE];
_curReadState = new ReadState(); _curReadState = new ReadState();
_inboundListener = new InboundListener(); _inboundListener = new InboundListener();

View File

@ -14,17 +14,20 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.Vector; import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import net.i2p.crypto.SigType; import net.i2p.crypto.SigType;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.router.RouterAddress; import net.i2p.data.router.RouterAddress;
@ -104,15 +107,31 @@ public class NTCPTransport extends TransportImpl {
private long _lastBadSkew; private long _lastBadSkew;
private static final long[] RATES = { 10*60*1000 }; private static final long[] RATES = { 10*60*1000 };
// Opera doesn't have the char, TODO check UA
//private static final String THINSP = "&thinsp;/&thinsp;";
private static final String THINSP = " / ";
/** /**
* RI sigtypes supported in 0.9.16 * RI sigtypes supported in 0.9.16
*/ */
public static final String MIN_SIGTYPE_VERSION = "0.9.16"; public static final String MIN_SIGTYPE_VERSION = "0.9.16";
// NTCP2 stuff
public static final String STYLE = "NTCP";
private static final String STYLE2 = "NTCP2";
private static final String PROP_NTCP2_ENABLE = "i2np.ntcp2.enable";
private static final boolean DEFAULT_NTCP2_ENABLE = false;
private boolean _enableNTCP2;
private static final String NTCP2_PROTO_SHORT = "NXK2CS";
private static final String OPT_NTCP2_SK = 'N' + NTCP2_PROTO_SHORT + "2s";
private static final int NTCP2_INT_VERSION = 2;
private static final String NTCP2_VERSION = Integer.toString(NTCP2_INT_VERSION);
/** b64 static private key */
private static final String PROP_NTCP2_SP = "i2np.ntcp2.sp";
/** b64 static IV */
private static final String PROP_NTCP2_IV = "i2np.ntcp2.iv";
private static final int NTCP2_IV_LEN = 16;
private static final int NTCP2_KEY_LEN = 32;
private final byte[] _ntcp2StaticPrivkey;
private final byte[] _ntcp2StaticIV;
private final String _b64Ntcp2StaticPubkey;
private final String _b64Ntcp2StaticIV;
public NTCPTransport(RouterContext ctx, DHSessionKeyBuilder.Factory dh) { public NTCPTransport(RouterContext ctx, DHSessionKeyBuilder.Factory dh) {
super(ctx); super(ctx);
@ -202,6 +221,51 @@ public class NTCPTransport extends TransportImpl {
_nearCapacityBid = new SharedBid(90); // not better than ssu - save our conns for inbound _nearCapacityBid = new SharedBid(90); // not better than ssu - save our conns for inbound
_nearCapacityCostBid = new SharedBid(105); _nearCapacityCostBid = new SharedBid(105);
_transientFail = new SharedBid(TransportBid.TRANSIENT_FAIL); _transientFail = new SharedBid(TransportBid.TRANSIENT_FAIL);
_enableNTCP2 = ctx.getProperty(PROP_NTCP2_ENABLE, DEFAULT_NTCP2_ENABLE);
if (_enableNTCP2) {
boolean shouldSave = false;
byte[] priv = null;
byte[] iv = null;
String b64Pub = null;
String b64IV = null;
String s = ctx.getProperty(PROP_NTCP2_SP);
if (s != null) {
priv = Base64.decode(s);
}
if (priv == null || priv.length != NTCP2_KEY_LEN) {
priv = new byte[NTCP2_KEY_LEN];
ctx.random().nextBytes(priv);
shouldSave = true;
}
s = ctx.getProperty(PROP_NTCP2_IV);
if (s != null) {
iv = Base64.decode(s);
b64IV = s;
}
if (iv == null || iv.length != NTCP2_IV_LEN) {
iv = new byte[NTCP2_IV_LEN];
ctx.random().nextBytes(iv);
shouldSave = true;
}
if (shouldSave) {
Map<String, String> changes = new HashMap<String, String>(2);
String b64Priv = Base64.encode(priv);
b64IV = Base64.encode(iv);
changes.put(PROP_NTCP2_SP, b64Priv);
changes.put(PROP_NTCP2_IV, b64IV);
ctx.router().saveConfig(changes, null);
}
_ntcp2StaticPrivkey = priv;
_ntcp2StaticIV = iv;
_b64Ntcp2StaticPubkey = "TODO"; // priv->pub
_b64Ntcp2StaticIV = b64IV;
} else {
_ntcp2StaticPrivkey = null;
_ntcp2StaticIV = null;
_b64Ntcp2StaticPubkey = null;
_b64Ntcp2StaticIV = null;
}
} }
/** /**
@ -242,11 +306,16 @@ public class NTCPTransport extends TransportImpl {
isNew = true; isNew = true;
RouterAddress addr = getTargetAddress(target); RouterAddress addr = getTargetAddress(target);
if (addr != null) { if (addr != null) {
con = new NTCPConnection(_context, this, ident, addr); int ver = getNTCPVersion(addr);
//if (_log.shouldLog(Log.DEBUG)) if (ver != 0) {
// _log.debug("Send on a new con: " + con + " at " + addr + " for " + ih); con = new NTCPConnection(_context, this, ident, addr, ver);
// Note that outbound conns go in the map BEFORE establishment //if (_log.shouldLog(Log.DEBUG))
_conByIdent.put(ih, con); // _log.debug("Send on a new con: " + con + " at " + addr + " for " + ih);
// Note that outbound conns go in the map BEFORE establishment
_conByIdent.put(ih, con);
} else {
fail = true;
}
} else { } else {
// race, RI changed out from under us // race, RI changed out from under us
// call afterSend below outside of conLock // call afterSend below outside of conLock
@ -674,6 +743,7 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties(); OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress()); props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress());
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port)); props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port));
addNTCP2Options(props);
int cost = getDefaultCost(ia instanceof Inet6Address); int cost = getDefaultCost(ia instanceof Inet6Address);
myAddress = new RouterAddress(STYLE, props, cost); myAddress = new RouterAddress(STYLE, props, cost);
replaceAddress(myAddress); replaceAddress(myAddress);
@ -786,6 +856,7 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties(); OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, bindTo); props.setProperty(RouterAddress.PROP_HOST, bindTo);
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port)); props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port));
addNTCP2Options(props);
int cost = getDefaultCost(false); int cost = getDefaultCost(false);
myAddress = new RouterAddress(STYLE, props, cost); myAddress = new RouterAddress(STYLE, props, cost);
} }
@ -872,6 +943,16 @@ public class NTCPTransport extends TransportImpl {
public String getStyle() { return STYLE; } public String getStyle() { return STYLE; }
/**
* An alternate supported style, or null.
* @return "NTCP2" or null
* @since 0.9.35
*/
@Override
public String getAltStyle() {
return _enableNTCP2 ? STYLE2 : null;
}
/** /**
* Hook for NTCPConnection * Hook for NTCPConnection
*/ */
@ -982,11 +1063,63 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties(); OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, name); props.setProperty(RouterAddress.PROP_HOST, name);
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(p)); props.setProperty(RouterAddress.PROP_PORT, Integer.toString(p));
addNTCP2Options(props);
int cost = getDefaultCost(false); int cost = getDefaultCost(false);
RouterAddress addr = new RouterAddress(STYLE, props, cost); RouterAddress addr = new RouterAddress(STYLE, props, cost);
return addr; return addr;
} }
/**
* Add the required options to the properties for a NTCP2 address
*
* @since 0.9.35
*/
private void addNTCP2Options(Properties props) {
if (!_enableNTCP2)
return;
props.setProperty("i", _b64Ntcp2StaticIV);
props.setProperty("n", NTCP2_PROTO_SHORT);
props.setProperty("s", _b64Ntcp2StaticPubkey);
props.setProperty("v", NTCP2_VERSION);
}
/**
* Is NTCP2 enabled?
*
* @since 0.9.35
*/
boolean isNTCP2Enabled() { return _enableNTCP2; }
/**
* Get the valid NTCP version of this NTCP address.
*
* @return the valid version 1 or 2, or 0 if unusable
* @since 0.9.35
*/
private int getNTCPVersion(RouterAddress addr) {
int rv;
String style = addr.getTransportStyle();
if (style.equals(STYLE)) {
if (!_enableNTCP2)
return 1;
rv = 1;
} else if (style.equals(STYLE2)) {
if (!_enableNTCP2)
return 0;
rv = 2;
} else {
return 0;
}
if (addr.getOption("s") == null ||
addr.getOption("i") == null ||
!NTCP2_VERSION.equals(addr.getOption("v")) ||
!NTCP2_PROTO_SHORT.equals(addr.getOption("n"))) {
return (rv == 1) ? 1 : 0;
}
// todo validate s/i b64, or just catch it later?
return rv;
}
/** /**
* Return a single configured IP (as a String) or null if not configured or invalid. * Return a single configured IP (as a String) or null if not configured or invalid.
* Resolves a hostname to an IP. * Resolves a hostname to an IP.
@ -1171,6 +1304,7 @@ public class NTCPTransport extends TransportImpl {
int cost; int cost;
if (oldAddr == null) { if (oldAddr == null) {
cost = getDefaultCost(isIPv6); cost = getDefaultCost(isIPv6);
addNTCP2Options(newProps);
} else { } else {
cost = oldAddr.getCost(); cost = oldAddr.getCost();
newProps.putAll(oldAddr.getOptionsMap()); newProps.putAll(oldAddr.getOptionsMap());
@ -1463,8 +1597,6 @@ public class NTCPTransport extends TransportImpl {
_lastInboundIPv6 = 0; _lastInboundIPv6 = 0;
} }
public static final String STYLE = "NTCP";
public void renderStatusHTML(java.io.Writer out, int sortFlags) throws IOException {} public void renderStatusHTML(java.io.Writer out, int sortFlags) throws IOException {}
/** /**

View File

@ -18,7 +18,7 @@ import net.i2p.util.SimpleByteCache;
/** /**
* *
* We are Alice * NTCP 1 only. We are Alice.
* *
*/ */
class OutboundEstablishState extends EstablishBase { class OutboundEstablishState extends EstablishBase {
@ -48,6 +48,13 @@ class OutboundEstablishState extends EstablishBase {
receiveOutbound(src); receiveOutbound(src);
} }
/**
* Get the NTCP version
* @return 1
* @since 0.9.35
*/
public int getVersion() { return 1; }
/** /**
* We are Alice, so receive these bytes as part of an outbound connection. * We are Alice, so receive these bytes as part of an outbound connection.
* This method receives messages 2 and 4, and sends message 3. * This method receives messages 2 and 4, and sends message 3.