forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head bde4ef3680071b416b3528bad4c16245964f432a)
to branch 'i2p.i2p.zzz.test2' (head 183d2a30edd10165451d0cbbf75636f3b1dfda16)
This commit is contained in:
@@ -70,8 +70,7 @@ public class DataMessage extends FastI2NPMessageImpl {
|
||||
out[curIndex++] = 0x0;
|
||||
out[curIndex++] = 0x0;
|
||||
} else {
|
||||
byte len[] = DataHelper.toLong(4, _data.length);
|
||||
System.arraycopy(len, 0, out, curIndex, 4);
|
||||
DataHelper.toLong(out, curIndex, 4, _data.length);
|
||||
curIndex += 4;
|
||||
System.arraycopy(_data, 0, out, curIndex, _data.length);
|
||||
curIndex += _data.length;
|
||||
|
||||
@@ -404,33 +404,33 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
|
||||
System.arraycopy(_fromHash.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
|
||||
curIndex += Hash.HASH_LENGTH;
|
||||
// Generate the flag byte
|
||||
byte flag;
|
||||
if (_replyKey != null)
|
||||
flag = FLAG_ENCRYPT;
|
||||
else
|
||||
flag = 0;
|
||||
switch (_type) {
|
||||
case LS:
|
||||
flag |= FLAG_TYPE_LS;
|
||||
break;
|
||||
case RI:
|
||||
flag |= FLAG_TYPE_RI;
|
||||
break;
|
||||
case EXPL:
|
||||
flag |= FLAG_TYPE_EXPL;
|
||||
break;
|
||||
case ANY:
|
||||
default:
|
||||
// lookup type bits are 0
|
||||
break;
|
||||
}
|
||||
if (_replyTunnel != null) {
|
||||
byte flag = FLAG_TUNNEL;
|
||||
if (_replyKey != null)
|
||||
flag |= FLAG_ENCRYPT;
|
||||
switch (_type) {
|
||||
case LS:
|
||||
flag |= FLAG_TYPE_LS;
|
||||
break;
|
||||
case RI:
|
||||
flag |= FLAG_TYPE_RI;
|
||||
break;
|
||||
case EXPL:
|
||||
flag |= FLAG_TYPE_EXPL;
|
||||
break;
|
||||
case ANY:
|
||||
default:
|
||||
// flag is 0
|
||||
break;
|
||||
}
|
||||
flag |= FLAG_TUNNEL;
|
||||
out[curIndex++] = flag;
|
||||
byte id[] = DataHelper.toLong(4, _replyTunnel.getTunnelId());
|
||||
System.arraycopy(id, 0, out, curIndex, 4);
|
||||
DataHelper.toLong(out, curIndex, 4, _replyTunnel.getTunnelId());
|
||||
curIndex += 4;
|
||||
} else if (_replyKey != null) {
|
||||
out[curIndex++] = FLAG_ENCRYPT;
|
||||
} else {
|
||||
out[curIndex++] = 0x00;
|
||||
out[curIndex++] = flag;
|
||||
}
|
||||
if ( (_dontIncludePeers == null) || (_dontIncludePeers.isEmpty()) ) {
|
||||
out[curIndex++] = 0x0;
|
||||
@@ -439,9 +439,8 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
|
||||
int size = _dontIncludePeers.size();
|
||||
if (size > MAX_NUM_PEERS)
|
||||
throw new I2NPMessageException("Too many peers: " + size);
|
||||
byte len[] = DataHelper.toLong(2, size);
|
||||
out[curIndex++] = len[0];
|
||||
out[curIndex++] = len[1];
|
||||
DataHelper.toLong(out, curIndex, 2, size);
|
||||
curIndex += 2;
|
||||
for (Hash peer : _dontIncludePeers) {
|
||||
System.arraycopy(peer.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
|
||||
curIndex += Hash.HASH_LENGTH;
|
||||
|
||||
@@ -103,8 +103,8 @@ public class DatabaseSearchReplyMessage extends FastI2NPMessageImpl {
|
||||
|
||||
System.arraycopy(_key.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
|
||||
curIndex += Hash.HASH_LENGTH;
|
||||
byte len[] = DataHelper.toLong(1, _peerHashes.size());
|
||||
out[curIndex++] = len[0];
|
||||
DataHelper.toLong(out, curIndex, 1, _peerHashes.size());
|
||||
curIndex++;
|
||||
for (int i = 0; i < getNumReplies(); i++) {
|
||||
System.arraycopy(getReply(i).getData(), 0, out, curIndex, Hash.HASH_LENGTH);
|
||||
curIndex += Hash.HASH_LENGTH;
|
||||
|
||||
@@ -204,16 +204,14 @@ public class DatabaseStoreMessage extends FastI2NPMessageImpl {
|
||||
System.arraycopy(getKey().getData(), 0, out, curIndex, Hash.HASH_LENGTH);
|
||||
curIndex += Hash.HASH_LENGTH;
|
||||
out[curIndex++] = (byte) type;
|
||||
byte tok[] = DataHelper.toLong(4, _replyToken);
|
||||
System.arraycopy(tok, 0, out, curIndex, 4);
|
||||
DataHelper.toLong(out, curIndex, 4, _replyToken);
|
||||
curIndex += 4;
|
||||
|
||||
if (_replyToken > 0) {
|
||||
long replyTunnel = 0;
|
||||
if (_replyTunnel != null)
|
||||
replyTunnel = _replyTunnel.getTunnelId();
|
||||
byte id[] = DataHelper.toLong(4, replyTunnel);
|
||||
System.arraycopy(id, 0, out, curIndex, 4);
|
||||
DataHelper.toLong(out, curIndex, 4, replyTunnel);
|
||||
curIndex += 4;
|
||||
System.arraycopy(_replyGateway.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
|
||||
curIndex += Hash.HASH_LENGTH;
|
||||
@@ -221,9 +219,8 @@ public class DatabaseStoreMessage extends FastI2NPMessageImpl {
|
||||
|
||||
// _byteCache initialized in calculateWrittenLength
|
||||
if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
||||
byte len[] = DataHelper.toLong(2, _byteCache.length);
|
||||
out[curIndex++] = len[0];
|
||||
out[curIndex++] = len[1];
|
||||
DataHelper.toLong(out, curIndex, 2, _byteCache.length);
|
||||
curIndex += 2;
|
||||
}
|
||||
System.arraycopy(_byteCache, 0, out, curIndex, _byteCache.length);
|
||||
curIndex += _byteCache.length;
|
||||
|
||||
@@ -54,8 +54,7 @@ public class GarlicMessage extends FastI2NPMessageImpl {
|
||||
}
|
||||
/** write the message body to the output array, starting at the given index */
|
||||
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
|
||||
byte len[] = DataHelper.toLong(4, _data.length);
|
||||
System.arraycopy(len, 0, out, curIndex, 4);
|
||||
DataHelper.toLong(out, curIndex, 4, _data.length);
|
||||
curIndex += 4;
|
||||
System.arraycopy(_data, 0, out, curIndex, _data.length);
|
||||
curIndex += _data.length;
|
||||
|
||||
@@ -113,7 +113,7 @@ public class RouterInfo extends DatabaseEntry {
|
||||
return _published;
|
||||
}
|
||||
|
||||
protected KeysAndCert getKeysAndCert() {
|
||||
public KeysAndCert getKeysAndCert() {
|
||||
return _identity;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,13 +25,18 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import gnu.getopt.Getopt;
|
||||
|
||||
import net.i2p.crypto.SigUtil;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.message.GarlicMessageHandler;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.startup.CreateRouterInfoJob;
|
||||
@@ -101,6 +106,8 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
public final static String PROP_HIDDEN_HIDDEN = "router.isHidden";
|
||||
public final static String PROP_DYNAMIC_KEYS = "router.dynamicKeys";
|
||||
public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress";
|
||||
private static final String PROP_IB_RANDOM_KEY = TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY + TunnelPoolSettings.PROP_RANDOM_KEY;
|
||||
private static final String PROP_OB_RANDOM_KEY = TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY + TunnelPoolSettings.PROP_RANDOM_KEY;
|
||||
public final static String DNS_CACHE_TIME = "" + (5*60);
|
||||
private static final String EVENTLOG = "eventlog.txt";
|
||||
private static final String PROP_JBIGI = "jbigi.loadedResource";
|
||||
@@ -343,6 +350,10 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
SimpleByteCache.clearAll();
|
||||
Destination.clearCache();
|
||||
Translate.clearCache();
|
||||
Hash.clearCache();
|
||||
PublicKey.clearCache();
|
||||
SigningPublicKey.clearCache();
|
||||
SigUtil.clearCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,6 +497,18 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
//_sessionKeyPersistenceHelper.startup();
|
||||
//_context.adminManager().startup();
|
||||
_context.blocklist().startup();
|
||||
|
||||
synchronized(_configFileLock) {
|
||||
// persistent key for peer ordering since 0.9.17
|
||||
if (!_config.containsKey(PROP_IB_RANDOM_KEY)) {
|
||||
byte rk[] = new byte[32];
|
||||
_context.random().nextBytes(rk);
|
||||
_config.put(PROP_IB_RANDOM_KEY, Base64.encode(rk));
|
||||
_context.random().nextBytes(rk);
|
||||
_config.put(PROP_OB_RANDOM_KEY, Base64.encode(rk));
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// let the timestamper get us sync'ed
|
||||
// this will block for quite a while on a disconnected machine
|
||||
@@ -711,9 +734,11 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
}
|
||||
|
||||
// now that we have random ports, keeping the same port would be bad
|
||||
synchronized(this) {
|
||||
synchronized(_configFileLock) {
|
||||
removeConfigSetting(UDPTransport.PROP_INTERNAL_PORT);
|
||||
removeConfigSetting(UDPTransport.PROP_EXTERNAL_PORT);
|
||||
removeConfigSetting(PROP_IB_RANDOM_KEY);
|
||||
removeConfigSetting(PROP_OB_RANDOM_KEY);
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 10;
|
||||
public final static long BUILD = 6;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.NativeBigInteger;
|
||||
import net.i2p.util.RandomSource;
|
||||
@@ -28,7 +29,7 @@ public class TunnelPoolSettings {
|
||||
private boolean _allowZeroHop;
|
||||
private int _IPRestriction;
|
||||
private final Properties _unknownOptions;
|
||||
private final Hash _randomKey;
|
||||
private Hash _randomKey;
|
||||
private int _priority;
|
||||
|
||||
/** prefix used to override the router's defaults for clients */
|
||||
@@ -51,6 +52,8 @@ public class TunnelPoolSettings {
|
||||
public static final String PROP_ALLOW_ZERO_HOP = "allowZeroHop";
|
||||
public static final String PROP_IP_RESTRICTION = "IPRestriction";
|
||||
public static final String PROP_PRIORITY = "priority";
|
||||
/** @since 0.9.17 */
|
||||
public static final String PROP_RANDOM_KEY = "randomKey";
|
||||
|
||||
public static final int DEFAULT_QUANTITY = 2;
|
||||
public static final int DEFAULT_BACKUP_QUANTITY = 0;
|
||||
@@ -204,7 +207,11 @@ public class TunnelPoolSettings {
|
||||
/** what destination is this a client tunnel for (or null if exploratory) */
|
||||
public Hash getDestination() { return _destination; }
|
||||
|
||||
/** random key used for peer ordering */
|
||||
/**
|
||||
* random key used for peer ordering
|
||||
*
|
||||
* @return non-null
|
||||
*/
|
||||
public Hash getRandomKey() { return _randomKey; }
|
||||
|
||||
/** what user supplied name was given to the client connected (can be null) */
|
||||
@@ -265,6 +272,10 @@ public class TunnelPoolSettings {
|
||||
int def = _isExploratory ? EXPLORATORY_PRIORITY : 0;
|
||||
int max = _isExploratory ? EXPLORATORY_PRIORITY : MAX_PRIORITY;
|
||||
_priority = Math.min(max, Math.max(MIN_PRIORITY, getInt(value, def)));
|
||||
} else if (name.equalsIgnoreCase(prefix + PROP_RANDOM_KEY)) {
|
||||
byte[] rk = Base64.decode(value);
|
||||
if (rk != null && rk.length == Hash.HASH_LENGTH)
|
||||
_randomKey = new Hash(rk);
|
||||
} else
|
||||
_unknownOptions.setProperty(name.substring(prefix.length()), value);
|
||||
}
|
||||
|
||||
@@ -13,12 +13,14 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.crypto.KeyStoreUtil;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.I2PSSLSocketFactory;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
|
||||
@@ -174,6 +176,7 @@ class SSLClientListenerRunner extends ClientListenerRunner {
|
||||
_log.info("Listening on port " + _port + " of the specific interface: " + listenInterface);
|
||||
rv = _factory.createServerSocket(_port, 0, InetAddress.getByName(listenInterface));
|
||||
}
|
||||
I2PSSLSocketFactory.setProtocolsAndCiphers((SSLServerSocket) rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -110,10 +110,10 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
||||
private static final long SESSION_TAG_EXPIRATION_WINDOW = 90 * 1000;
|
||||
|
||||
/**
|
||||
* a few MB? how about 16MB!
|
||||
* a few MB? how about 24 MB!
|
||||
* This is the max size of _inboundTagSets.
|
||||
*/
|
||||
public final static int MAX_INBOUND_SESSION_TAGS = 500 * 1000; // this will consume at most a few MB
|
||||
public final static int MAX_INBOUND_SESSION_TAGS = 750 * 1000;
|
||||
|
||||
/**
|
||||
* This was 100 since 0.6.1.10 (50 before that). It's important because:
|
||||
@@ -584,7 +584,7 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
||||
int recent = 0;
|
||||
int tags = 0;
|
||||
int toRemove = overage * 2;
|
||||
_log.log(Log.CRIT, "TOO MANY SESSION TAGS! Starting cleanup, overage = " + overage);
|
||||
_log.logAlways(Log.WARN, "TOO MANY SESSION TAGS! Starting cleanup, overage = " + overage);
|
||||
List<TagSet> removed = new ArrayList<TagSet>(toRemove);
|
||||
synchronized (_inboundTagSets) {
|
||||
for (TagSet set : _inboundTagSets.values()) {
|
||||
@@ -593,12 +593,18 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
||||
absurd++;
|
||||
if (size > 100)
|
||||
large++;
|
||||
if (now >= set.getDate())
|
||||
if (set.getDate() - now < 3*60*1000) {
|
||||
// expiration is 12 minutes, so these are older than 9 minutes
|
||||
old++;
|
||||
else if (set.getDate() - now > 10*60*1000)
|
||||
removed.add(set);
|
||||
continue;
|
||||
} else if (set.getDate() - now > 8*60*1000) {
|
||||
// expiration is 12 minutes, so these were created in last 4 minutes
|
||||
recent++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((removed.size() < (toRemove)) || (now >= set.getDate()))
|
||||
if (removed.size() < toRemove)
|
||||
removed.add(set);
|
||||
}
|
||||
for (int i = 0; i < removed.size(); i++) {
|
||||
@@ -609,11 +615,10 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.CRIT))
|
||||
_log.log(Log.CRIT, "TOO MANY SESSION TAGS! removing " + removed
|
||||
_log.logAlways(Log.WARN, "TOO MANY SESSION TAGS! removed " + removed.size()
|
||||
+ " tag sets arbitrarily, with " + tags + " tags,"
|
||||
+ "where there are " + old + " long lasting sessions, "
|
||||
+ recent + " ones created in the last minute, and "
|
||||
+ recent + " ones created in the last few minutes, and "
|
||||
+ large + " sessions with more than 100 tags (and "
|
||||
+ absurd + " with more than 1000!), leaving a total of "
|
||||
+ _inboundTagSets.size() + " tags behind");
|
||||
|
||||
@@ -220,6 +220,11 @@ class OutboundClientMessageJobHelper {
|
||||
ackClove.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
DeliveryStatusMessage dsm = buildDSM(ctx, replyToken);
|
||||
GarlicMessage msg = wrapDSM(ctx, skm, dsm);
|
||||
if (msg == null) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Failed to wrap ack clove");
|
||||
return null;
|
||||
}
|
||||
ackClove.setPayload(msg);
|
||||
// this does nothing, the clove is not separately encrypted
|
||||
//ackClove.setRecipient(ctx.router().getRouterInfo());
|
||||
|
||||
@@ -1334,12 +1334,13 @@ public class ProfileOrganizer {
|
||||
* others.
|
||||
* @return 0-3
|
||||
*/
|
||||
private static int getSubTier(Hash peer, Hash randomKey) {
|
||||
byte[] data = new byte[Hash.HASH_LENGTH + 4];
|
||||
private int getSubTier(Hash peer, Hash randomKey) {
|
||||
// input is first 36 bytes; output is last 32 bytes
|
||||
byte[] data = new byte[Hash.HASH_LENGTH + 4 + Hash.HASH_LENGTH];
|
||||
System.arraycopy(peer.getData(), 0, data, 0, Hash.HASH_LENGTH);
|
||||
System.arraycopy(randomKey.getData(), 0, data, Hash.HASH_LENGTH, 4);
|
||||
Hash rh = SHA256Generator.getInstance().calculateHash(data);
|
||||
return rh.getData()[0] & 0x03;
|
||||
_context.sha().calculateHash(data, 0, Hash.HASH_LENGTH + 4, data, Hash.HASH_LENGTH + 4);
|
||||
return data[Hash.HASH_LENGTH + 4] & 0x03;
|
||||
}
|
||||
|
||||
public boolean isSelectable(Hash peer) {
|
||||
|
||||
@@ -72,9 +72,9 @@ public class CreateRouterInfoJob extends JobImpl {
|
||||
|
||||
/**
|
||||
* Writes 6 files: router.info (standard RI format),
|
||||
* router.keys2, and 4 individual key files under keyBackup/
|
||||
* router.keys.dat, and 4 individual key files under keyBackup/
|
||||
*
|
||||
* router.keys2 file format: This is the
|
||||
* router.keys.dat file format: This is the
|
||||
* same "eepPriv.dat" format used by the client code,
|
||||
* as documented in PrivateKeyFile.
|
||||
*
|
||||
|
||||
@@ -66,7 +66,7 @@ class LoadRouterInfoJob extends JobImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads router.info and router.keys2 or router.keys.
|
||||
* Loads router.info and either router.keys.dat or router.keys.
|
||||
*
|
||||
* See CreateRouterInfoJob for file formats
|
||||
*/
|
||||
|
||||
@@ -121,6 +121,7 @@ class EstablishState {
|
||||
private static final int HXY_SIZE = 32; //Hash.HASH_LENGTH;
|
||||
private static final int HXY_TSB_PAD_SIZE = HXY_SIZE + 4 + 12; // 48
|
||||
|
||||
private static final Object _stateLock = new Object();
|
||||
protected State _state;
|
||||
|
||||
private enum State {
|
||||
@@ -193,6 +194,13 @@ class EstablishState {
|
||||
_curDecrypted = SimpleByteCache.acquire(AES_SIZE);
|
||||
}
|
||||
|
||||
/** @since 0.9.16 */
|
||||
private void changeState(State state) {
|
||||
synchronized (_stateLock) {
|
||||
_state = state;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the contents of the buffer as part of the handshake. if the
|
||||
* handshake is completed and there is more data remaining, the data are
|
||||
@@ -203,8 +211,10 @@ class EstablishState {
|
||||
* will return it to the pool.
|
||||
*/
|
||||
public synchronized void receive(ByteBuffer src) {
|
||||
if (_state == State.VERIFIED || _state == State.CORRUPT)
|
||||
throw new IllegalStateException(prefix() + "received unexpected data on " + _con);
|
||||
synchronized(_stateLock) {
|
||||
if (_state == State.VERIFIED || _state == State.CORRUPT)
|
||||
throw new IllegalStateException(prefix() + "received unexpected data on " + _con);
|
||||
}
|
||||
if (!src.hasRemaining())
|
||||
return; // nothing to receive
|
||||
|
||||
@@ -229,6 +239,9 @@ class EstablishState {
|
||||
* will return it to the pool.
|
||||
*
|
||||
* Caller must synch.
|
||||
*
|
||||
* FIXME none of the _state comparisons use _stateLock, but whole thing
|
||||
* is synchronized, should be OK. See isComplete()
|
||||
*/
|
||||
private void receiveInbound(ByteBuffer src) {
|
||||
while (_state == State.IB_INIT && src.hasRemaining()) {
|
||||
@@ -243,7 +256,7 @@ class EstablishState {
|
||||
// }
|
||||
//}
|
||||
if (_received >= XY_SIZE)
|
||||
_state = State.IB_GOT_X;
|
||||
changeState(State.IB_GOT_X);
|
||||
}
|
||||
while (_state == State.IB_GOT_X && src.hasRemaining()) {
|
||||
int i = _received - XY_SIZE;
|
||||
@@ -252,7 +265,7 @@ class EstablishState {
|
||||
_hX_xor_bobIdentHash[i] = c;
|
||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("recv bih" + (int)c + " received=" + _received);
|
||||
if (i >= HXY_SIZE - 1)
|
||||
_state = State.IB_GOT_HX;
|
||||
changeState(State.IB_GOT_HX);
|
||||
}
|
||||
|
||||
if (_state == State.IB_GOT_HX) {
|
||||
@@ -325,7 +338,7 @@ class EstablishState {
|
||||
System.arraycopy(_e_hXY_tsB, 0, write, XY_SIZE, HXY_TSB_PAD_SIZE);
|
||||
|
||||
// ok, now that is prepared, we want to actually send it, so make sure we are up for writing
|
||||
_state = State.IB_SENT_Y;
|
||||
changeState(State.IB_SENT_Y);
|
||||
_transport.getPumper().wantsWrite(_con, write);
|
||||
if (!src.hasRemaining()) return;
|
||||
} catch (DHSessionKeyBuilder.InvalidPublicParameterException e) {
|
||||
@@ -370,7 +383,7 @@ class EstablishState {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(prefix() + "got the RI size: " + sz);
|
||||
_aliceIdentSize = sz;
|
||||
_state = State.IB_GOT_RI_SIZE;
|
||||
changeState(State.IB_GOT_RI_SIZE);
|
||||
|
||||
// We must defer the calculations for total size of the message until
|
||||
// we get the full alice ident so
|
||||
@@ -401,7 +414,7 @@ class EstablishState {
|
||||
fail("Unsupported sig type");
|
||||
return;
|
||||
}
|
||||
_state = State.IB_GOT_RI;
|
||||
changeState(State.IB_GOT_RI);
|
||||
// handle variable signature size
|
||||
_sz_aliceIdent_tsA_padding_aliceSigSize = 2 + _aliceIdentSize + 4 + type.getSigLen();
|
||||
int rem = (_sz_aliceIdent_tsA_padding_aliceSigSize % AES_SIZE);
|
||||
@@ -453,6 +466,9 @@ class EstablishState {
|
||||
* will return it to the pool.
|
||||
*
|
||||
* Caller must synch.
|
||||
*
|
||||
* FIXME none of the _state comparisons use _stateLock, but whole thing
|
||||
* is synchronized, should be OK. See isComplete()
|
||||
*/
|
||||
private void receiveOutbound(ByteBuffer src) {
|
||||
// recv Y+E(H(X+Y)+tsB, sk, Y[239:255])
|
||||
@@ -466,7 +482,7 @@ class EstablishState {
|
||||
_dh.getSessionKey(); // force the calc
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(prefix()+"DH session key calculated (" + _dh.getSessionKey().toBase64() + ")");
|
||||
_state = State.OB_GOT_Y;
|
||||
changeState(State.OB_GOT_Y);
|
||||
} catch (DHSessionKeyBuilder.InvalidPublicParameterException e) {
|
||||
_context.statManager().addRateData("ntcp.invalidDH", 1);
|
||||
fail("Invalid X", e);
|
||||
@@ -500,7 +516,7 @@ class EstablishState {
|
||||
return;
|
||||
}
|
||||
SimpleByteCache.release(h);
|
||||
_state = State.OB_GOT_HXY;
|
||||
changeState(State.OB_GOT_HXY);
|
||||
_tsB = DataHelper.fromLong(hXY_tsB, HXY_SIZE, 4); // their (Bob's) timestamp in seconds
|
||||
_tsA = (_context.clock().now() + 500) / 1000; // our (Alice's) timestamp in seconds
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -573,7 +589,7 @@ class EstablishState {
|
||||
//_log.debug(prefix() + "encrypted response to Bob: " + Base64.encode(_prevEncrypted));
|
||||
//}
|
||||
// send 'er off (when the bw limiter says, etc)
|
||||
_state = State.OB_SENT_RI;
|
||||
changeState(State.OB_SENT_RI);
|
||||
_transport.getPumper().wantsWrite(_con, _prevEncrypted);
|
||||
}
|
||||
}
|
||||
@@ -607,7 +623,7 @@ class EstablishState {
|
||||
_received++;
|
||||
|
||||
if (off >= _e_bobSig.length) {
|
||||
_state = State.OB_GOT_SIG;
|
||||
changeState(State.OB_GOT_SIG);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug(prefix() + "received E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev): " + Base64.encode(_e_bobSig));
|
||||
byte bobSig[] = new byte[_e_bobSig.length];
|
||||
@@ -634,7 +650,6 @@ class EstablishState {
|
||||
_context.statManager().addRateData("ntcp.invalidSignature", 1);
|
||||
fail("Signature was invalid - attempt to spoof " + _con.getRemotePeer().calculateHash().toBase64() + "?");
|
||||
} else {
|
||||
_state = State.VERIFIED;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(prefix() + "signature verified from Bob. done!");
|
||||
prepareExtra(src);
|
||||
@@ -642,11 +657,12 @@ class EstablishState {
|
||||
System.arraycopy(_prevEncrypted, _prevEncrypted.length-AES_SIZE, nextWriteIV, 0, AES_SIZE);
|
||||
// this does not copy the nextWriteIV, do not release to cache
|
||||
_con.finishOutboundEstablishment(_dh.getSessionKey(), (_tsA-_tsB), nextWriteIV, _e_bobSig); // skew in seconds
|
||||
releaseBufs();
|
||||
releaseBufs(true);
|
||||
// if socket gets closed this will be null - prevent NPE
|
||||
InetAddress ia = _con.getChannel().socket().getInetAddress();
|
||||
if (ia != null)
|
||||
_transport.setIP(_con.getRemotePeer().calculateHash(), ia.getAddress());
|
||||
changeState(State.VERIFIED);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -655,10 +671,24 @@ class EstablishState {
|
||||
}
|
||||
|
||||
/** did the handshake fail for some reason? */
|
||||
public synchronized boolean isCorrupt() { return _state == State.CORRUPT; }
|
||||
public boolean isCorrupt() {
|
||||
synchronized(_stateLock) {
|
||||
return _state == State.CORRUPT;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return is the handshake complete and valid? */
|
||||
public synchronized boolean isComplete() { return _state == State.VERIFIED; }
|
||||
/**
|
||||
* If synchronized on this, fails with
|
||||
* deadlocks from all over via CSFI.isEstablished().
|
||||
* Also CSFI.getFramedAveragePeerClockSkew().
|
||||
*
|
||||
* @return is the handshake complete and valid?
|
||||
*/
|
||||
public boolean isComplete() {
|
||||
synchronized(_stateLock) {
|
||||
return _state == State.VERIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We are Alice.
|
||||
@@ -667,13 +697,17 @@ class EstablishState {
|
||||
* This method sends message #1 to Bob.
|
||||
*/
|
||||
public synchronized void prepareOutbound() {
|
||||
if (_state == State.OB_INIT) {
|
||||
boolean shouldSend;
|
||||
synchronized(_stateLock) {
|
||||
shouldSend = _state == State.OB_INIT;
|
||||
}
|
||||
if (shouldSend) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(prefix() + "send X");
|
||||
byte toWrite[] = new byte[XY_SIZE + _hX_xor_bobIdentHash.length];
|
||||
System.arraycopy(_X, 0, toWrite, 0, XY_SIZE);
|
||||
System.arraycopy(_hX_xor_bobIdentHash, 0, toWrite, XY_SIZE, _hX_xor_bobIdentHash.length);
|
||||
_state = State.OB_SENT_X;
|
||||
changeState(State.OB_SENT_X);
|
||||
_transport.getPumper().wantsWrite(_con, toWrite);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@@ -815,7 +849,6 @@ class EstablishState {
|
||||
_log.debug(prefix()+"Clock skew: " + diff + " ms");
|
||||
}
|
||||
|
||||
_state = State.VERIFIED;
|
||||
sendInboundConfirm(_aliceIdent, tsA);
|
||||
_con.setRemotePeer(_aliceIdent);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -824,9 +857,10 @@ class EstablishState {
|
||||
System.arraycopy(_e_bobSig, _e_bobSig.length-AES_SIZE, iv, 0, AES_SIZE);
|
||||
// this does not copy the IV, do not release to cache
|
||||
_con.finishInboundEstablishment(_dh.getSessionKey(), (tsA-_tsB), iv, _prevEncrypted); // skew in seconds
|
||||
releaseBufs();
|
||||
releaseBufs(true);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(prefix()+"Verified remote peer as " + _aliceIdent.calculateHash());
|
||||
changeState(State.VERIFIED);
|
||||
} else {
|
||||
_context.statManager().addRateData("ntcp.invalidInboundSignature", 1);
|
||||
fail("Peer verification failed - spoof of " + _aliceIdent.calculateHash() + "?");
|
||||
@@ -917,28 +951,30 @@ class EstablishState {
|
||||
|
||||
/** Caller must synch. */
|
||||
private void fail(String reason, Exception e, boolean bySkew) {
|
||||
if (_state == State.CORRUPT || _state == State.VERIFIED)
|
||||
return;
|
||||
_state = State.CORRUPT;
|
||||
synchronized(_stateLock) {
|
||||
if (_state == State.CORRUPT || _state == State.VERIFIED)
|
||||
return;
|
||||
changeState(State.CORRUPT);
|
||||
}
|
||||
_failedBySkew = bySkew;
|
||||
_err = reason;
|
||||
_e = e;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(prefix()+"Failed to establish: " + _err, e);
|
||||
releaseBufs();
|
||||
releaseBufs(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only call once. Caller must synch.
|
||||
* @since 0.9.16
|
||||
*/
|
||||
private void releaseBufs() {
|
||||
private void releaseBufs(boolean isVerified) {
|
||||
// null or longer for OB
|
||||
if (_prevEncrypted != null && _prevEncrypted.length == AES_SIZE)
|
||||
SimpleByteCache.release(_prevEncrypted);
|
||||
// Do not release _curEncrypted if verified, it is passed to
|
||||
// NTCPConnection to use as the IV
|
||||
if (_state != State.VERIFIED)
|
||||
if (!isVerified)
|
||||
SimpleByteCache.release(_curEncrypted);
|
||||
SimpleByteCache.release(_curDecrypted);
|
||||
SimpleByteCache.release(_hX_xor_bobIdentHash);
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.TreeSet;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
@@ -47,6 +48,7 @@ import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
/**
|
||||
* The NIO TCP transport
|
||||
@@ -101,6 +103,12 @@ public class NTCPTransport extends TransportImpl {
|
||||
//private static final String THINSP = " / ";
|
||||
private static final String THINSP = " / ";
|
||||
|
||||
/**
|
||||
* RI sigtypes supported in 0.9.16
|
||||
*/
|
||||
private static final String MIN_SIGTYPE_VERSION = "0.9.16";
|
||||
|
||||
|
||||
public NTCPTransport(RouterContext ctx, DHSessionKeyBuilder.Factory dh) {
|
||||
super(ctx);
|
||||
_dhFactory = dh;
|
||||
@@ -356,11 +364,25 @@ public class NTCPTransport extends TransportImpl {
|
||||
}
|
||||
|
||||
// Check for supported sig type
|
||||
if (toAddress.getIdentity().getSigningPublicKey().getType() == null) {
|
||||
SigType type = toAddress.getIdentity().getSigType();
|
||||
if (type == null || !type.isAvailable()) {
|
||||
markUnreachable(peer);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Can we connect to them if we are not DSA?
|
||||
RouterInfo us = _context.router().getRouterInfo();
|
||||
if (us != null) {
|
||||
RouterIdentity id = us.getIdentity();
|
||||
if (id.getSigType() != SigType.DSA_SHA1) {
|
||||
String v = toAddress.getOption("router.version");
|
||||
if (v != null && VersionComparator.comp(v, MIN_SIGTYPE_VERSION) < 0) {
|
||||
markUnreachable(peer);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowConnection()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("no bid when trying to send to " + peer + ", max connection limit reached");
|
||||
|
||||
@@ -958,8 +958,8 @@ class EstablishmentManager {
|
||||
if (!oldId.equals(newId)) {
|
||||
_outboundStates.remove(oldId);
|
||||
_outboundStates.put(newId, state);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("RR replaced " + oldId + " with " + newId + ", claimed address was " + claimed);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("RR replaced " + oldId + " with " + newId + ", claimed address was " + claimed);
|
||||
}
|
||||
//
|
||||
if (claimed != null)
|
||||
@@ -980,17 +980,17 @@ class EstablishmentManager {
|
||||
if (state != null) {
|
||||
boolean sendNow = state.receiveHolePunch();
|
||||
if (sendNow) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Hole punch from " + state + ", sending SessionRequest now");
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Hole punch from " + state + ", sending SessionRequest now");
|
||||
notifyActivity();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Hole punch from " + state + ", already sent SessionRequest");
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Hole punch from " + state + ", already sent SessionRequest");
|
||||
}
|
||||
} else {
|
||||
// HolePunch received before RelayResponse, and we didn't know the IP/port, or it changed
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No state found for hole punch from " + from + " port " + fromPort);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("No state found for hole punch from " + from + " port " + fromPort);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@ class InboundEstablishState {
|
||||
DataHelper.toLong(signed, off, 4, _sentRelayTag);
|
||||
off += 4;
|
||||
DataHelper.toLong(signed, off, 4, _receivedSignedOnTime);
|
||||
Signature sig = new Signature(_receivedSignature);
|
||||
Signature sig = new Signature(_receivedUnconfirmedIdentity.getSigType(), _receivedSignature);
|
||||
boolean ok = _context.dsa().verifySignature(sig, signed, _receivedUnconfirmedIdentity.getSigningPublicKey());
|
||||
if (ok) {
|
||||
// todo partial spoof detection - get peer.calculateHash(),
|
||||
|
||||
@@ -72,10 +72,14 @@ class InboundMessageState implements CDQEntry {
|
||||
_log = ctx.logManager().getLog(InboundMessageState.class);
|
||||
_messageId = messageId;
|
||||
_from = from;
|
||||
if (data.readMessageIsLast(dataFragment))
|
||||
_fragments = new ByteArray[1 + data.readMessageFragmentNum(dataFragment)];
|
||||
else
|
||||
if (data.readMessageIsLast(dataFragment)) {
|
||||
int num = 1 + data.readMessageFragmentNum(dataFragment);
|
||||
if (num > MAX_FRAGMENTS)
|
||||
throw new DataFormatException("corrupt - too many fragments: " + num);
|
||||
_fragments = new ByteArray[num];
|
||||
} else {
|
||||
_fragments = new ByteArray[MAX_FRAGMENTS];
|
||||
}
|
||||
_lastFragment = -1;
|
||||
_completeSize = -1;
|
||||
_receiveBegin = ctx.clock().now();
|
||||
@@ -222,8 +226,10 @@ class InboundMessageState implements CDQEntry {
|
||||
return _completeSize;
|
||||
}
|
||||
|
||||
/** FIXME synch here or PeerState.fetchPartialACKs() */
|
||||
public ACKBitfield createACKBitfield() {
|
||||
int sz = (_lastFragment >= 0) ? _lastFragment + 1 : _fragments.length;
|
||||
int last = _lastFragment;
|
||||
int sz = (last >= 0) ? last + 1 : _fragments.length;
|
||||
return new PartialBitfield(_messageId, _fragments, sz);
|
||||
}
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ class OutboundEstablishState {
|
||||
return _sentX;
|
||||
}
|
||||
|
||||
/**x
|
||||
/**
|
||||
* The remote side (Bob) - note that in some places he's called Charlie.
|
||||
* Warning - may change after introduction. May be null before introduction.
|
||||
*/
|
||||
@@ -618,8 +618,8 @@ class OutboundEstablishState {
|
||||
if (_requestSentCount > 0)
|
||||
return false;
|
||||
long now = _context.clock().now();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(toString() + " accelerating SessionRequest by " + (_nextSend - now) + " ms");
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(toString() + " accelerating SessionRequest by " + (_nextSend - now) + " ms");
|
||||
_nextSend = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -434,7 +434,9 @@ class PacketBuilder {
|
||||
off += 4;
|
||||
for (int curByte = 0; curByte < size; curByte++) {
|
||||
if (curByte + 1 < size)
|
||||
data[off] |= (byte)(1 << 7);
|
||||
data[off] = (byte)(1 << 7);
|
||||
else
|
||||
data[off] = 0;
|
||||
|
||||
for (int curBit = 0; curBit < 7; curBit++) {
|
||||
if (bitfield.received(curBit + 7*curByte))
|
||||
@@ -467,7 +469,7 @@ class PacketBuilder {
|
||||
DataHelper.toLong(data, off, 4, state.getMessageId());
|
||||
off += 4;
|
||||
|
||||
data[off] |= fragment << 1;
|
||||
data[off] = (byte) (fragment << 1);
|
||||
if (fragment == state.getFragmentCount() - 1)
|
||||
data[off] |= 1; // isLast
|
||||
off++;
|
||||
@@ -621,8 +623,7 @@ class PacketBuilder {
|
||||
off++;
|
||||
for (int i = 0; i < ackBitfields.size(); i++) {
|
||||
ACKBitfield bitfield = ackBitfields.get(i);
|
||||
// no, this will corrupt the packet
|
||||
//if (bitfield.receivedComplete()) continue;
|
||||
if (bitfield.receivedComplete()) continue;
|
||||
DataHelper.toLong(data, off, 4, bitfield.getMessageId());
|
||||
off += 4;
|
||||
// only send what we have to
|
||||
@@ -633,7 +634,9 @@ class PacketBuilder {
|
||||
size++;
|
||||
for (int curByte = 0; curByte < size; curByte++) {
|
||||
if (curByte + 1 < size)
|
||||
data[off] |= (byte)(1 << 7);
|
||||
data[off] = (byte)(1 << 7);
|
||||
else
|
||||
data[off] = 0;
|
||||
|
||||
for (int curBit = 0; curBit < 7; curBit++) {
|
||||
if (bitfield.received(curBit + 7*curByte))
|
||||
@@ -878,7 +881,7 @@ class PacketBuilder {
|
||||
}
|
||||
|
||||
// now for the body
|
||||
data[off] |= fragmentNum << 4;
|
||||
data[off] = (byte) (fragmentNum << 4);
|
||||
data[off] |= (numFragments & 0xF);
|
||||
off++;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Map;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.router.transport.TransportUtil;
|
||||
import net.i2p.util.LHMCache;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
@@ -100,7 +101,7 @@ class UDPAddress {
|
||||
int p;
|
||||
try {
|
||||
p = Integer.parseInt(port);
|
||||
if (p < UDPTransport.MIN_PEER_PORT || p > 65535) continue;
|
||||
if (!TransportUtil.isValidPort(p)) continue;
|
||||
} catch (NumberFormatException nfe) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DatabaseEntry;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
@@ -51,6 +52,7 @@ import net.i2p.util.Log;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
/**
|
||||
* The SSU transport
|
||||
@@ -198,6 +200,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
//private static final String THINSP = " / ";
|
||||
private static final String THINSP = " / ";
|
||||
|
||||
/**
|
||||
* RI sigtypes supported in 0.9.16, but due to a bug in InboundEstablishState
|
||||
* fixed in 0.9.17, we cannot connect out to routers before that version.
|
||||
*/
|
||||
private static final String MIN_SIGTYPE_VERSION = "0.9.17";
|
||||
|
||||
|
||||
public UDPTransport(RouterContext ctx, DHSessionKeyBuilder.Factory dh) {
|
||||
super(ctx);
|
||||
_dhFactory = dh;
|
||||
@@ -1558,11 +1567,25 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
|
||||
// Check for supported sig type
|
||||
if (toAddress.getIdentity().getSigningPublicKey().getType() == null) {
|
||||
SigType type = toAddress.getIdentity().getSigType();
|
||||
if (type == null || !type.isAvailable()) {
|
||||
markUnreachable(to);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Can we connect to them if we are not DSA?
|
||||
RouterInfo us = _context.router().getRouterInfo();
|
||||
if (us != null) {
|
||||
RouterIdentity id = us.getIdentity();
|
||||
if (id.getSigType() != SigType.DSA_SHA1) {
|
||||
String v = toAddress.getOption("router.version");
|
||||
if (v != null && VersionComparator.comp(v, MIN_SIGTYPE_VERSION) < 0) {
|
||||
markUnreachable(to);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowConnection())
|
||||
return _cachedBid[TRANSIENT_FAIL_BID];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user