Profiles: Pull same-IP detection into a utility class, for use by netdb

This commit is contained in:
zzz
2016-11-16 18:05:40 +00:00
parent 37d3204e43
commit 68e5fd6d08
4 changed files with 132 additions and 68 deletions

View File

@ -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 = 8;
public final static long BUILD = 9;
/** for example "-test" */
public final static String EXTRA = "";

View File

@ -24,6 +24,7 @@ import net.i2p.data.router.RouterInfo;
import net.i2p.router.NetworkDatabaseFacade;
import net.i2p.router.RouterContext;
import net.i2p.router.tunnel.pool.TunnelPeerSelector;
import net.i2p.router.util.MaskedIPSet;
import net.i2p.router.util.RandomIterator;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
@ -1245,7 +1246,7 @@ public class ProfileOrganizer {
*/
private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches, int mask) {
List<Hash> all = new ArrayList<Hash>(peers.keySet());
Set<String> IPSet = new HashSet<String>(8);
MaskedIPSet IPSet = new MaskedIPSet(8);
// use RandomIterator to avoid shuffling the whole thing
for (Iterator<Hash> iter = new RandomIterator<Hash>(all); (matches.size() < howMany) && iter.hasNext(); ) {
Hash peer = iter.next();
@ -1277,77 +1278,14 @@ public class ProfileOrganizer {
* @param mask is 1-4 (number of bytes to match)
* @param IPMatches all IPs so far, modified by this routine
*/
private boolean notRestricted(Hash peer, Set<String> IPSet, int mask) {
Set<String> peerIPs = maskedIPSet(peer, mask);
if (containsAny(IPSet, peerIPs))
private boolean notRestricted(Hash peer, MaskedIPSet IPSet, int mask) {
Set<String> peerIPs = new MaskedIPSet(_context, peer, mask);
if (IPSet.containsAny(peerIPs))
return false;
IPSet.addAll(peerIPs);
return true;
}
/**
* The Set of IPs for this peer, with a given mask.
* Includes the comm system's record of the IP, and all netDb addresses.
*
* As of 0.9.24, returned set will include netdb family as well.
*
* @return an opaque set of masked IPs for this peer
*/
private Set<String> maskedIPSet(Hash peer, int mask) {
Set<String> rv = new HashSet<String>(4);
byte[] commIP = _context.commSystem().getIP(peer);
if (commIP != null)
rv.add(maskedIP(commIP, mask));
RouterInfo pinfo = _context.netDb().lookupRouterInfoLocally(peer);
if (pinfo == null)
return rv;
Collection<RouterAddress> paddr = pinfo.getAddresses();
for (RouterAddress pa : paddr) {
byte[] pib = pa.getIP();
if (pib == null) continue;
rv.add(maskedIP(pib, mask));
}
String family = pinfo.getOption("family");
if (family != null) {
// TODO should KNDF put a family-verified indicator in the RI,
// after checking the sig, or does it matter?
// What's the threat here of not avoid ding a router
// falsely claiming to be in the family?
// Prefix with something so an IP can't be spoofed
rv.add('x' + family);
}
return rv;
}
/**
* generate an arbitrary unique value for this ip/mask (mask = 1-4)
* If IPv6, force mask = 6.
*/
private static String maskedIP(byte[] ip, int mask) {
final StringBuilder buf = new StringBuilder(1 + (mask*2));
final char delim;
if (ip.length == 16) {
mask = 6;
delim = ':';
} else {
delim = '.';
}
buf.append(delim);
buf.append(Long.toHexString(DataHelper.fromLong(ip, 0, mask)));
return buf.toString();
}
/** does a contain any of the elements in b? */
private static <T> boolean containsAny(Set<T> a, Set<T> b) {
if (a.isEmpty() || b.isEmpty())
return false;
for (T o : b) {
if (a.contains(o))
return true;
}
return false;
}
/**
* @param randomKey used for deterministic random partitioning into subtiers
* @param subTierMode 2-7:

View File

@ -0,0 +1,92 @@
package net.i2p.router.util;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.router.RouterAddress;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
/**
* Used for detection of routers with matching IPs or family.
* Moved out of ProfileOrganizer for use in netdb also.
*
* @since 0.9.28
*/
public class MaskedIPSet extends HashSet<String> {
public MaskedIPSet() {
super();
}
public MaskedIPSet(int initialCapacity) {
super(initialCapacity);
}
/**
* The Set of IPs for this peer, with a given mask.
* Includes the comm system's record of the IP, and all netDb addresses.
*
* As of 0.9.24, returned set will include netdb family as well.
*
* @param mask is 1-4 (number of bytes to match)
* @return an opaque set of masked IPs for this peer
*/
public MaskedIPSet(RouterContext ctx, Hash peer, int mask) {
super(4);
byte[] commIP = ctx.commSystem().getIP(peer);
if (commIP != null)
add(maskedIP(commIP, mask));
RouterInfo pinfo = ctx.netDb().lookupRouterInfoLocally(peer);
if (pinfo == null)
return;
Collection<RouterAddress> paddr = pinfo.getAddresses();
for (RouterAddress pa : paddr) {
byte[] pib = pa.getIP();
if (pib == null) continue;
add(maskedIP(pib, mask));
}
String family = pinfo.getOption("family");
if (family != null) {
// TODO should KNDF put a family-verified indicator in the RI,
// after checking the sig, or does it matter?
// What's the threat here of not avoid ding a router
// falsely claiming to be in the family?
// Prefix with something so an IP can't be spoofed
add('x' + family);
}
}
/**
* generate an arbitrary unique value for this ip/mask (mask = 1-4)
* If IPv6, force mask = 6.
* @param mask is 1-4 (number of bytes to match)
*/
private static String maskedIP(byte[] ip, int mask) {
final StringBuilder buf = new StringBuilder(1 + (mask*2));
final char delim;
if (ip.length == 16) {
mask = 6;
delim = ':';
} else {
delim = '.';
}
buf.append(delim);
buf.append(Long.toHexString(DataHelper.fromLong(ip, 0, mask)));
return buf.toString();
}
/** does this contain any of the elements in b? */
public boolean containsAny(Set<String> b) {
if (isEmpty() || b.isEmpty())
return false;
for (String s : b) {
if (contains(s))
return true;
}
return false;
}
}