From ef44c36f0c3b9615f5a69bba77a1439c71ade266 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Tue, 20 Nov 2018 14:14:34 +0000 Subject: [PATCH] GeoIP: Hook maxmind geoip2 code into transport lookup. --- .../src/net/i2p/router/transport/GeoIP.java | 120 ++++++++++++++++-- 1 file changed, 108 insertions(+), 12 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/GeoIP.java b/router/java/src/net/i2p/router/transport/GeoIP.java index 8265b5b9c6..086c071eeb 100644 --- a/router/java/src/net/i2p/router/transport/GeoIP.java +++ b/router/java/src/net/i2p/router/transport/GeoIP.java @@ -16,8 +16,10 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; +import com.maxmind.db.CHMCache; import com.maxmind.geoip.InvalidDatabaseException; import com.maxmind.geoip.LookupService; +import com.maxmind.geoip2.DatabaseReader; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; @@ -64,13 +66,15 @@ public class GeoIP { public static final String PROP_GEOIP_DIR = "geoip.dir"; public static final String GEOIP_DIR_DEFAULT = "geoip"; static final String GEOIP_FILE_DEFAULT = "geoip.txt"; + static final String GEOIP2_FILE_DEFAULT = "GeoLite2-Country.mmdb"; static final String COUNTRY_FILE_DEFAULT = "countries.txt"; public static final String PROP_IP_COUNTRY = "i2np.lastCountry"; public static final String PROP_DEBIAN_GEOIP = "geoip.dat"; public static final String PROP_DEBIAN_GEOIPV6 = "geoip.v6.dat"; private static final String DEBIAN_GEOIP_FILE = "/usr/share/GeoIP/GeoIP.dat"; private static final String DEBIAN_GEOIPV6_FILE = "/usr/share/GeoIP/GeoIPv6.dat"; - private static final boolean ENABLE_DEBIAN = !(SystemVersion.isWindows() || SystemVersion.isAndroid()); + private static final boolean DISABLE_DEBIAN = false; + private static final boolean ENABLE_DEBIAN = !DISABLE_DEBIAN && !(SystemVersion.isWindows() || SystemVersion.isAndroid()); /** maxmind API */ private static final String UNKNOWN_COUNTRY_CODE = "--"; @@ -147,6 +151,8 @@ public class GeoIP { public void run() { if (_lock.getAndSet(true)) return; + File geoip2 = getGeoIP2(); + DatabaseReader dbr = null; try { // clear the negative cache every few runs, to prevent it from getting too big if (((++_lookupRunCount) % CLEAR) == 0) @@ -158,19 +164,20 @@ public class GeoIP { Arrays.sort(search); File f = new File(_context.getProperty(PROP_DEBIAN_GEOIP, DEBIAN_GEOIP_FILE)); if (ENABLE_DEBIAN && f.exists()) { - // Maxmind database + // Maxmind v1 database LookupService ls = null; try { ls = new LookupService(f, LookupService.GEOIP_STANDARD); for (int i = 0; i < search.length; i++) { - long ip = search[i].longValue(); + Long ipl = search[i]; + long ip = ipl.longValue(); // returns upper case or "--" String uc = ls.getCountry(ip).getCode(); if (!uc.equals(UNKNOWN_COUNTRY_CODE)) { String cached = _codeCache.get(uc.toLowerCase(Locale.US)); - _IPToCountry.put(search[i], cached); + _IPToCountry.put(ipl, cached); } else { - _notFound.add(search[i]); + _notFound.add(ipl); } } } catch (IOException ioe) { @@ -180,6 +187,25 @@ public class GeoIP { } finally { if (ls != null) ls.close(); } + } else if (geoip2 != null) { + // Maxmind v2 database + try { + dbr = openGeoIP2(geoip2); + for (int i = 0; i < search.length; i++) { + Long ipl = search[i]; + String ipv4 = toV4(ipl); + // returns upper case or null + String uc = dbr.country(ipv4); + if (uc != null) { + String cached = _codeCache.get(uc.toLowerCase(Locale.US)); + _IPToCountry.put(ipl, cached); + } else { + _notFound.add(ipl); + } + } + } catch (IOException ioe) { + _log.error("GeoIP2 failure", ioe); + } } else { // Tor-style database String[] countries = readGeoIPFile(search); @@ -198,12 +224,13 @@ public class GeoIP { Arrays.sort(search); File f = new File(_context.getProperty(PROP_DEBIAN_GEOIPV6, DEBIAN_GEOIPV6_FILE)); if (ENABLE_DEBIAN && f.exists()) { - // Maxmind database + // Maxmind v1 database LookupService ls = null; try { ls = new LookupService(f, LookupService.GEOIP_STANDARD); for (int i = 0; i < search.length; i++) { - long ip = search[i].longValue(); + Long ipl = search[i]; + long ip = ipl.longValue(); String ipv6 = toV6(ip); // returns upper case or "--" String uc = ls.getCountryV6(ipv6).getCode(); @@ -212,9 +239,9 @@ public class GeoIP { String cached = _codeCache.get(lc); if (cached == null) cached = lc; - _IPToCountry.put(search[i], cached); + _IPToCountry.put(ipl, cached); } else { - _notFound.add(search[i]); + _notFound.add(ipl); } } } catch (IOException ioe) { @@ -224,8 +251,28 @@ public class GeoIP { } finally { if (ls != null) ls.close(); } - } else { - // Tor-style database + } else if (geoip2 != null) { + // Maxmind v2 database + try { + if (dbr == null) + dbr = openGeoIP2(geoip2); + for (int i = 0; i < search.length; i++) { + Long ipl = search[i]; + String ipv6 = toV6(ipl); + // returns upper case or null + String uc = dbr.country(ipv6); + if (uc != null) { + String cached = _codeCache.get(uc.toLowerCase(Locale.US)); + _IPToCountry.put(ipl, cached); + } else { + _notFound.add(ipl); + } + } + } catch (IOException ioe) { + _log.error("GeoIP2 failure", ioe); + } + } else { + // I2P format IPv6 database String[] countries = GeoIPv6.readGeoIPFile(_context, search, _codeCache); for (int i = 0; i < countries.length; i++) { if (countries[i] != null) @@ -236,11 +283,45 @@ public class GeoIP { } } } finally { + if (dbr != null) try { dbr.close(); } catch (IOException ioe) {} _lock.set(false); } } } + /** + * Get the GeoIP2 database file + * + * @return null if not found + * @since 0.9.38 + */ + private File getGeoIP2() { + String geoDir = _context.getProperty(PROP_GEOIP_DIR, GEOIP_DIR_DEFAULT); + File geoFile = new File(geoDir); + if (!geoFile.isAbsolute()) + geoFile = new File(_context.getBaseDir(), geoDir); + geoFile = new File(geoFile, GEOIP2_FILE_DEFAULT); + if (!geoFile.exists()) { + if (_log.shouldLog(Log.WARN)) + _log.warn("GeoIP2 file not found: " + geoFile.getAbsolutePath()); + return null; + } + return geoFile; + } + + /** + * Open a GeoIP2 database + * @since 0.9.38 + */ + private DatabaseReader openGeoIP2(File geoFile) throws IOException { + DatabaseReader.Builder b = new DatabaseReader.Builder(geoFile); + b.withCache(new CHMCache(256)); + DatabaseReader rv = b.build(); + if (_log.shouldDebug()) + _log.debug("Opened GeoIP2 Database, Metadata: " + rv.getMetadata()); + return rv; + } + /** * Read in and parse the country file. * The file need not be sorted. @@ -473,6 +554,21 @@ public class GeoIP { } } + /** + * @return e.g. 1.2.3.4 + * @since 0.9.38 for maxmind + */ + private static String toV4(long ip) { + StringBuilder buf = new StringBuilder(15); + for (int i = 0; i < 4; i++) { + buf.append(Long.toString((ip >> ((3-i)*8)) & 0xff)); + if (i == 3) + break; + buf.append('.'); + } + return buf.toString(); + } + /** * @return e.g. aabb:ccdd:eeff:1122:: * @since 0.9.26 for maxmind @@ -498,7 +594,7 @@ public class GeoIP { /*** public static void main(String args[]) { - GeoIP g = new GeoIP(new Router().getContext()); + GeoIP g = new GeoIP(I2PAppContext.getGlobalContext()); String tests[] = {"0.0.0.0", "0.0.0.1", "0.0.0.2", "0.0.0.255", "1.0.0.0", "94.3.3.3", "77.1.2.3", "127.0.0.0", "127.127.127.127", "128.0.0.0", "89.8.9.3", "72.5.6.8", "217.4.9.7", "175.107.027.107", "135.6.5.2", -- GitLab