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

@ -491,6 +491,23 @@ public class RouterInfo extends DatabaseEntry {
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
*/

View File

@ -660,11 +660,16 @@ public abstract class TransportImpl implements Transport {
* @since IPv6
*/
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())
return rv;
if (rv.size() > 1) {
// Shuffle so everybody doesn't use the first one
if (rv.size() > 1)
Collections.shuffle(rv, _context.random());
TransportUtil.IPv6Config config = getIPv6Config();
int adj;
@ -679,13 +684,17 @@ public abstract class TransportImpl implements Transport {
}
****/
break;
case IPV6_NOT_PREFERRED:
adj = 1; break;
default:
case IPV6_ENABLED:
adj = 0; break;
case IPV6_PREFERRED:
adj = -1; break;
case IPV6_ONLY:
adj = -10;
/**** IPv6 addresses will be rejected in isPubliclyRoutable()
@ -697,8 +706,8 @@ public abstract class TransportImpl implements Transport {
****/
break;
}
if (rv.size() > 1)
Collections.sort(rv, new AddrComparator(adj));
}
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
*/

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
*
* All data must be copied out of the buffer as Reader.processRead()
@ -337,12 +344,16 @@ abstract class EstablishBase implements EstablishState {
_state = State.VERIFIED;
}
@Override public void prepareOutbound() {
Log log =RouterContext.getCurrentContext().logManager().getLog(VerifiedEstablishState.class);
public int getVersion() { return 1; }
@Override
public void prepareOutbound() {
Log log = RouterContext.getCurrentContext().logManager().getLog(VerifiedEstablishState.class);
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;
}
@Override public void prepareOutbound() {
Log log =RouterContext.getCurrentContext().logManager().getLog(VerifiedEstablishState.class);
public int getVersion() { return 1; }
@Override
public void prepareOutbound() {
Log log = RouterContext.getCurrentContext().logManager().getLog(VerifiedEstablishState.class);
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();
/**
* Get the NTCP version
* @return 1, 2, or 0 if unknown
* @since 0.9.35
*/
public int getVersion();
/**
* Release resources on timeout.
* @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 {
@ -69,6 +69,13 @@ class InboundEstablishState extends EstablishBase {
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
* 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
*
* @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;
_log = ctx.logManager().getLog(getClass());
_created = ctx.clock().now();
@ -216,7 +218,10 @@ public class NTCPConnection implements Closeable {
//_outbound = new CoDelPriorityBlockingQueue(ctx, "NTCP-Connection", 32);
_outbound = new PriBlockingQueue<OutNetMessage>(ctx, "NTCP-Connection", 32);
_isInbound = false;
//if (version == 1)
_establishState = new OutboundEstablishState(ctx, transport, this);
//else
// _establishState = // TODO
_decryptBlockBuf = new byte[BLOCK_SIZE];
_curReadState = new ReadState();
_inboundListener = new InboundListener();

View File

@ -14,17 +14,20 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.crypto.SigType;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.router.RouterAddress;
@ -104,15 +107,31 @@ public class NTCPTransport extends TransportImpl {
private long _lastBadSkew;
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
*/
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) {
super(ctx);
@ -202,6 +221,51 @@ public class NTCPTransport extends TransportImpl {
_nearCapacityBid = new SharedBid(90); // not better than ssu - save our conns for inbound
_nearCapacityCostBid = new SharedBid(105);
_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;
RouterAddress addr = getTargetAddress(target);
if (addr != null) {
con = new NTCPConnection(_context, this, ident, addr);
int ver = getNTCPVersion(addr);
if (ver != 0) {
con = new NTCPConnection(_context, this, ident, addr, ver);
//if (_log.shouldLog(Log.DEBUG))
// _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 {
// race, RI changed out from under us
// call afterSend below outside of conLock
@ -674,6 +743,7 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress());
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port));
addNTCP2Options(props);
int cost = getDefaultCost(ia instanceof Inet6Address);
myAddress = new RouterAddress(STYLE, props, cost);
replaceAddress(myAddress);
@ -786,6 +856,7 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, bindTo);
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port));
addNTCP2Options(props);
int cost = getDefaultCost(false);
myAddress = new RouterAddress(STYLE, props, cost);
}
@ -872,6 +943,16 @@ public class NTCPTransport extends TransportImpl {
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
*/
@ -982,11 +1063,63 @@ public class NTCPTransport extends TransportImpl {
OrderedProperties props = new OrderedProperties();
props.setProperty(RouterAddress.PROP_HOST, name);
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(p));
addNTCP2Options(props);
int cost = getDefaultCost(false);
RouterAddress addr = new RouterAddress(STYLE, props, cost);
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.
* Resolves a hostname to an IP.
@ -1171,6 +1304,7 @@ public class NTCPTransport extends TransportImpl {
int cost;
if (oldAddr == null) {
cost = getDefaultCost(isIPv6);
addNTCP2Options(newProps);
} else {
cost = oldAddr.getCost();
newProps.putAll(oldAddr.getOptionsMap());
@ -1463,8 +1597,6 @@ public class NTCPTransport extends TransportImpl {
_lastInboundIPv6 = 0;
}
public static final String STYLE = "NTCP";
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 {
@ -48,6 +48,13 @@ class OutboundEstablishState extends EstablishBase {
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.
* This method receives messages 2 and 4, and sends message 3.