From 68e5fd6d087bdce826f9a09d43e96f258368a404 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 16 Nov 2016 18:05:40 +0000 Subject: [PATCH] Profiles: Pull same-IP detection into a utility class, for use by netdb --- history.txt | 34 +++++++ .../src/net/i2p/router/RouterVersion.java | 2 +- .../router/peermanager/ProfileOrganizer.java | 72 +-------------- .../src/net/i2p/router/util/MaskedIPSet.java | 92 +++++++++++++++++++ 4 files changed, 132 insertions(+), 68 deletions(-) create mode 100644 router/java/src/net/i2p/router/util/MaskedIPSet.java diff --git a/history.txt b/history.txt index ce83a9a78d..77bf74c33c 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,37 @@ +2016-11-16 zzz + * Console: Remove dead home page links (ticket #1882) + * Profiles: Pull same-IP detection into a utility class + * Router: Add methods to verify and track members of our family + +2016-11-15 zzz + * Certs: Add Let's Encrypt ISRG Root X1 cert + +2016-11-14 zzz + * Logs: Fix output of dup message after 30 minutes + +2016-11-13 zzz + * Console: Add initial news to bottom of news page (ticket #1153) + * i2psnark: Periodically DHT nodes (ticket #1328) + * UPnP: + - Prevent exception on bad HTTP header (ticket #1480) + - Prevent NPE on socket creation fail (tickets #728, #1681) + +2016-11-12 zzz + * Console: + - Fix inadvertent config save when clicking sidebar + buttons on /configstats + - Add IPv6 firewalled setting on /confignet + * I2CP: Reduce error level on session closed while signing LS (ticket #1606) + * JRobin: Move DeallocationHelper logging from wrapper log to router log + * Profiles: Periodically save, delete old ones after saving (ticket #1328) + * Susimail: + - Add logout button to more pages (ticket #1374) + - Fix nonce error on login after logout + - Fix internal error after cancel button on settings form when not logged in + +2016-11-11 zzz + * Build: Truncate history.txt bundled in installers + 2016-11-10 zzz * Transport: Use NTCP for some outbound connections even before SSU minimums are met (ticket #1835) diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index a6204817e3..f2522cd4ed 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -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 = ""; diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java index c71f8b6a65..481cc18b62 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java @@ -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 peers, int howMany, Set toExclude, Set matches, int mask) { List all = new ArrayList(peers.keySet()); - Set IPSet = new HashSet(8); + MaskedIPSet IPSet = new MaskedIPSet(8); // use RandomIterator to avoid shuffling the whole thing for (Iterator iter = new RandomIterator(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 IPSet, int mask) { - Set peerIPs = maskedIPSet(peer, mask); - if (containsAny(IPSet, peerIPs)) + private boolean notRestricted(Hash peer, MaskedIPSet IPSet, int mask) { + Set 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 maskedIPSet(Hash peer, int mask) { - Set rv = new HashSet(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 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 boolean containsAny(Set a, Set 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: diff --git a/router/java/src/net/i2p/router/util/MaskedIPSet.java b/router/java/src/net/i2p/router/util/MaskedIPSet.java new file mode 100644 index 0000000000..bfcecfd7c3 --- /dev/null +++ b/router/java/src/net/i2p/router/util/MaskedIPSet.java @@ -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 { + + 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 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 b) { + if (isEmpty() || b.isEmpty()) + return false; + for (String s : b) { + if (contains(s)) + return true; + } + return false; + } +}