Transport:

Include Maxmind geoip-api-java library v1.3.1 (LGPL v2.1)
Use Maxmind database for geoip lookup if it exists
Debian:
Don't bundle geoip.txt and geoipv6.dat.gz, depend on geoip-database instead
This commit is contained in:
zzz
2016-03-27 12:55:28 +00:00
parent 97a6cbda3e
commit 169fd56527
12 changed files with 1405 additions and 24 deletions

View File

@@ -103,6 +103,9 @@ Public domain except as listed below:
http://creativecommons.org/licenses/by-sa/3.0/
This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/
GeoIP API 1.3.1:
See licenses/LICENSE-LGPLv2.1.txt
Installer:
Launch4j 3.0.1:

View File

@@ -699,7 +699,7 @@
windowtitle="I2P Anonymous Network - Java Documentation - Version ${release.number}">
<group title="Core SDK (i2p.jar)" packages="net.i2p:net.i2p.*:net.i2p.client:net.i2p.client.*:net.i2p.internal:net.i2p.internal.*:freenet.support.CPUInformation:org.bouncycastle.oldcrypto:org.bouncycastle.oldcrypto.*:gnu.crypto.*:gnu.getopt:gnu.gettext:com.nettgryppa.security:net.metanotion:net.metanotion.*:org.apache.http.conn.ssl:org.apache.http.conn.util:org.apache.http.util" />
<group title="Streaming Library" packages="net.i2p.client.streaming:net.i2p.client.streaming.impl" />
<group title="Router" packages="net.i2p.router:net.i2p.router.*:net.i2p.data.i2np:net.i2p.data.router:org.cybergarage:org.cybergarage.*:org.freenetproject:org.xlattice.crypto.filters" />
<group title="Router" packages="net.i2p.router:net.i2p.router.*:net.i2p.data.i2np:net.i2p.data.router:org.cybergarage:org.cybergarage.*:org.freenetproject:org.xlattice.crypto.filters:com.maxmind.geoip" />
<group title="Router Console" packages="net.i2p.router.web:net.i2p.router.update:net.i2p.router.news" />
<!-- apps and bridges starting here, alphabetical please -->
<group title="Addressbook Application" packages="net.i2p.addressbook" />
@@ -1478,10 +1478,13 @@
<copy file="build/jbigi.jar" todir="pkg-temp/lib/" />
</target>
<!-- GeoIP files and flag icons -->
<!-- GeoIP files -->
<target name="prepgeoupdate">
<copy file="installer/resources/geoip.txt" todir="pkg-temp/geoip/" />
<copy file="installer/resources/geoipv6.dat.gz" todir="pkg-temp/geoip/" />
<copy todir="pkg-temp/geoip/" >
<!-- We use a fileset here so it doesn't fail if the files don't exist.
As of 0.9.26, the files are not included in Debian/Ubuntu builds. -->
<fileset dir="installer/resources" includes="geoip.txt geoipv6.dat.gz" />
</copy>
<copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" />
<copy file="installer/resources/continents.txt" todir="pkg-temp/geoip/" />
<!--
@@ -1986,6 +1989,8 @@
<include name="**/**" />
<exclude name="debian/**"/>
<exclude name="**/*.sh"/>
<exclude name="installer/resources/geoip.txt"/>
<exclude name="installer/resources/geoipv6.dat.gz"/>
</tarfileset>
<tarfileset dir="../i2p-${Extended.Version}" prefix="/i2p-${Extended.Version}" filemode="755">
<exclude name="debian/**" />

4
debian/control vendored
View File

@@ -70,7 +70,9 @@ Architecture: all
Section: net
Priority: optional
Depends: ${misc:Depends}, ${java:Depends}, ${shlibs:Depends},
openjdk-8-jre-headless | openjdk-7-jre-headless | default-jre-headless | java8-runtime-headless | java7-runtime-headless, libecj-java
openjdk-8-jre-headless | openjdk-7-jre-headless | default-jre-headless | java8-runtime-headless | java7-runtime-headless,
libecj-java,
geoip-database
Replaces: i2p ( << 0.8.6-5)
Breaks: i2p (<< 0.8.6-5)
Recommends: libjbigi-jni, ttf-dejavu

2
debian/repack.sh vendored
View File

@@ -28,6 +28,8 @@ cp -f ${tarball} "$tarball.bkp"
echo "Filtering tarball contents..."
bzcat "$tarball" | tar --wildcards --delete '*/installer/lib/*' \
--delete '*/Slackware/*' \
--delete '*/installer/resources/geoip.txt' \
--delete '*/installer/resources/geoipv6.dat.gz' \
--delete '*/debian/*' > "$tdir/${fname}"
echo "Compressing filtered tarball..."

View File

@@ -0,0 +1,63 @@
/**
* Country.java
*
* Copyright (C) 2003 MaxMind LLC. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.maxmind.geoip;
/**
* Represents a country.
*
* @author Matt Tucker
*/
public class Country {
private String code;
private String name;
/**
* Creates a new Country.
*
* @param code
* the country code.
* @param name
* the country name.
*/
public Country(String code, String name) {
this.code = code;
this.name = name;
}
/**
* Returns the ISO two-letter country code of this country.
*
* @return the country code.
*/
public String getCode() {
return code;
}
/**
* Returns the name of this country.
*
* @return the country name.
*/
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,128 @@
/**
* DatabaseInfo.java
*
* Copyright (C) 2003 MaxMind LLC. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.maxmind.geoip;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Encapsulates metadata about the GeoIP database. The database has a date, is a
* premium or standard version, and is one of the following types:
*
* <ul>
* <li>Country edition -- this is the most common version of the database. It
* includes the name of the country and it's ISO country code given an IP
* address.
* <li>Region edition -- includes the country information as well as what U.S.
* state or Canadian province the IP address is from if the IP address is from
* the U.S. or Canada.
* <li>City edition -- includes country, region, city, postal code, latitude,
* and longitude information.
* <li>Org edition -- includes netblock owner.
* <li>ISP edition -- ISP information.
* </ul>
*
* @see com.maxmind.geoip.LookupService#getDatabaseInfo()
* @author Matt Tucker
*/
public class DatabaseInfo {
public static final int COUNTRY_EDITION = 1;
public static final int REGION_EDITION_REV0 = 7;
public static final int REGION_EDITION_REV1 = 3;
public static final int CITY_EDITION_REV0 = 6;
public static final int CITY_EDITION_REV1 = 2;
public static final int ORG_EDITION = 5;
public static final int ISP_EDITION = 4;
public static final int PROXY_EDITION = 8;
public static final int ASNUM_EDITION = 9;
public static final int NETSPEED_EDITION = 10;
public static final int DOMAIN_EDITION = 11;
public static final int COUNTRY_EDITION_V6 = 12;
public static final int ASNUM_EDITION_V6 = 21;
public static final int ISP_EDITION_V6 = 22;
public static final int ORG_EDITION_V6 = 23;
public static final int DOMAIN_EDITION_V6 = 24;
public static final int CITY_EDITION_REV1_V6 = 30;
public static final int CITY_EDITION_REV0_V6 = 31;
public static final int NETSPEED_EDITION_REV1 = 32;
public static final int NETSPEED_EDITION_REV1_V6 = 33;
private static SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
private String info;
/**
* Creates a new DatabaseInfo object given the database info String.
*
* @param info
*/
public DatabaseInfo(String info) {
this.info = info;
}
public int getType() {
if (info == null || info.length() == 0) {
return COUNTRY_EDITION;
} else {
// Get the type code from the database info string and then
// subtract 105 from the value to preserve compatability with
// databases from April 2003 and earlier.
return Integer.parseInt(info.substring(4, 7)) - 105;
}
}
/**
* Returns true if the database is the premium version.
*
* @return true if the premium version of the database.
*/
public boolean isPremium() {
return !info.contains("FREE");
}
/**
* Returns the date of the database.
*
* @return the date of the database.
*/
public Date getDate() {
for (int i = 0; i < info.length() - 9; i++) {
if (Character.isWhitespace(info.charAt(i))) {
String dateString = info.substring(i + 1, i + 9);
try {
synchronized (formatter) {
return formatter.parse(dateString);
}
} catch (ParseException pe) {
}
break;
}
}
return null;
}
@Override
public String toString() {
return info;
}
}

View File

@@ -0,0 +1,26 @@
package com.maxmind.geoip;
import java.io.IOException;
/**
* Signals that there was an issue reading from the database file due to
* unexpected data formatting. This generally suggests that the database is
* corrupt or otherwise not in a format supported by the reader.
*/
public final class InvalidDatabaseException extends RuntimeException {
/**
* @param message A message describing the reason why the exception was thrown.
*/
public InvalidDatabaseException(String message) {
super(message);
}
/**
* @param message A message describing the reason why the exception was thrown.
* @param cause The cause of the exception.
*/
public InvalidDatabaseException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,62 @@
/**
* Location.java
*
* Copyright (C) 2004 MaxMind LLC. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Lesser Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.maxmind.geoip;
public class Location {
public String countryCode;
public String countryName;
public String region;
public String city;
public String postalCode;
public float latitude;
public float longitude;
public int dma_code;
public int area_code;
public int metro_code;
private final static double EARTH_DIAMETER = 2 * 6378.2;
private final static double PI = 3.14159265;
private final static double RAD_CONVERT = PI / 180;
public double distance(Location loc) {
double delta_lat, delta_lon;
double temp;
float lat1 = latitude;
float lon1 = longitude;
float lat2 = loc.latitude;
float lon2 = loc.longitude;
// convert degrees to radians
lat1 *= RAD_CONVERT;
lat2 *= RAD_CONVERT;
// find the deltas
delta_lat = lat2 - lat1;
delta_lon = (lon2 - lon1) * RAD_CONVERT;
// Find the great circle distance
temp = Math.pow(Math.sin(delta_lat / 2), 2) + Math.cos(lat1)
* Math.cos(lat2) * Math.pow(Math.sin(delta_lon / 2), 2);
return EARTH_DIAMETER
* Math.atan2(Math.sqrt(temp), Math.sqrt(1 - temp));
}
}

View File

@@ -0,0 +1,988 @@
/*
* LookupService.java
*
* Copyright (C) 2003 MaxMind LLC. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option) any
* later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.maxmind.geoip;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Arrays;
import java.util.List;
/**
* Provides a lookup service for information based on an IP address. The
* location of a database file is supplied when creating a lookup service
* instance. The edition of the database determines what information is
* available about an IP address. See the DatabaseInfo class for further
* details.
* <p>
*
* The following code snippet demonstrates looking up the country that an IP
* address is from:
*
* <pre>
* // First, create a LookupService instance with the location of the database.
* LookupService lookupService = new LookupService(&quot;c:\\geoip.dat&quot;);
* // Assume we have a String ipAddress (in dot-decimal form).
* Country country = lookupService.getCountry(ipAddress);
* System.out.println(&quot;The country is: &quot; + country.getName());
* System.out.println(&quot;The country code is: &quot; + country.getCode());
* </pre>
*
* In general, a single LookupService instance should be created and then reused
* repeatedly.
* <p>
*
* <i>Tip:</i> Those deploying the GeoIP API as part of a web application may
* find it difficult to pass in a File to create the lookup service, as the
* location of the database may vary per deployment or may even be part of the
* web-application. In this case, the database should be added to the classpath
* of the web-app. For example, by putting it into the WEB-INF/classes directory
* of the web application. The following code snippet demonstrates how to create
* a LookupService using a database that can be found on the classpath:
*
* <pre>
* String fileName = getClass().getResource(&quot;/GeoIP.dat&quot;).toExternalForm()
* .substring(6);
* LookupService lookupService = new LookupService(fileName);
* </pre>
*
* @author Matt Tucker (matt@jivesoftware.com)
*/
public class LookupService {
/**
* Database file.
*/
private RandomAccessFile file;
private final File databaseFile;
/**
* Information about the database.
*/
private DatabaseInfo databaseInfo;
private static final Charset charset = Charset.forName("ISO-8859-1");
private final CharsetDecoder charsetDecoder = charset.newDecoder();
/**
* The database type. Default is the country edition.
*/
private byte databaseType = DatabaseInfo.COUNTRY_EDITION;
private int[] databaseSegments;
private int recordLength;
private int dboptions;
private byte[] dbbuffer;
private byte[] index_cache;
private long mtime;
private int last_netmask;
private static final int US_OFFSET = 1;
private static final int CANADA_OFFSET = 677;
private static final int WORLD_OFFSET = 1353;
private static final int FIPS_RANGE = 360;
private static final int COUNTRY_BEGIN = 16776960;
private static final int STATE_BEGIN_REV0 = 16700000;
private static final int STATE_BEGIN_REV1 = 16000000;
private static final int STRUCTURE_INFO_MAX_SIZE = 20;
private static final int DATABASE_INFO_MAX_SIZE = 100;
public static final int GEOIP_STANDARD = 0;
public static final int GEOIP_MEMORY_CACHE = 1;
public static final int GEOIP_CHECK_CACHE = 2;
public static final int GEOIP_INDEX_CACHE = 4;
public static final int GEOIP_UNKNOWN_SPEED = 0;
public static final int GEOIP_DIALUP_SPEED = 1;
public static final int GEOIP_CABLEDSL_SPEED = 2;
public static final int GEOIP_CORPORATE_SPEED = 3;
private static final int SEGMENT_RECORD_LENGTH = 3;
private static final int STANDARD_RECORD_LENGTH = 3;
private static final int ORG_RECORD_LENGTH = 4;
private static final int MAX_RECORD_LENGTH = 4;
private static final int MAX_ORG_RECORD_LENGTH = 300;
private static final int FULL_RECORD_LENGTH = 60;
private final Country UNKNOWN_COUNTRY = new Country("--", "N/A");
private static final String[] countryCode = { "--", "AP", "EU", "AD", "AE",
"AF", "AG", "AI", "AL", "AM", "CW", "AO", "AQ", "AR", "AS", "AT",
"AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI",
"BJ", "BM", "BN", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ",
"CA", "CC", "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN",
"CO", "CR", "CU", "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM",
"DO", "DZ", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "FI", "FJ",
"FK", "FM", "FO", "FR", "SX", "GA", "GB", "GD", "GE", "GF", "GH",
"GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW",
"GY", "HK", "HM", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN",
"IO", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE", "KG", "KH",
"KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC",
"LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD",
"MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS",
"MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF",
"NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA", "PE",
"PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW",
"PY", "QA", "RE", "RO", "RU", "RW", "SA", "SB", "SC", "SD", "SE",
"SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "ST",
"SV", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", "TK", "TM",
"TN", "TO", "TL", "TR", "TT", "TV", "TW", "TZ", "UA", "UG", "UM",
"US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF",
"WS", "YE", "YT", "RS", "ZA", "ZM", "ME", "ZW", "A1", "A2", "O1",
"AX", "GG", "IM", "JE", "BL", "MF", "BQ", "SS", "O1" };
private static final String[] countryName = { "N/A", "Asia/Pacific Region",
"Europe", "Andorra", "United Arab Emirates", "Afghanistan",
"Antigua and Barbuda", "Anguilla", "Albania", "Armenia", "Curacao",
"Angola", "Antarctica", "Argentina", "American Samoa", "Austria",
"Australia", "Aruba", "Azerbaijan", "Bosnia and Herzegovina",
"Barbados", "Bangladesh", "Belgium", "Burkina Faso", "Bulgaria",
"Bahrain", "Burundi", "Benin", "Bermuda", "Brunei Darussalam",
"Bolivia", "Brazil", "Bahamas", "Bhutan", "Bouvet Island",
"Botswana", "Belarus", "Belize", "Canada",
"Cocos (Keeling) Islands", "Congo, The Democratic Republic of the",
"Central African Republic", "Congo", "Switzerland",
"Cote D'Ivoire", "Cook Islands", "Chile", "Cameroon", "China",
"Colombia", "Costa Rica", "Cuba", "Cape Verde", "Christmas Island",
"Cyprus", "Czech Republic", "Germany", "Djibouti", "Denmark",
"Dominica", "Dominican Republic", "Algeria", "Ecuador", "Estonia",
"Egypt", "Western Sahara", "Eritrea", "Spain", "Ethiopia",
"Finland", "Fiji", "Falkland Islands (Malvinas)",
"Micronesia, Federated States of", "Faroe Islands", "France",
"Sint Maarten (Dutch part)", "Gabon", "United Kingdom", "Grenada",
"Georgia", "French Guiana", "Ghana", "Gibraltar", "Greenland",
"Gambia", "Guinea", "Guadeloupe", "Equatorial Guinea", "Greece",
"South Georgia and the South Sandwich Islands", "Guatemala",
"Guam", "Guinea-Bissau", "Guyana", "Hong Kong",
"Heard Island and McDonald Islands", "Honduras", "Croatia",
"Haiti", "Hungary", "Indonesia", "Ireland", "Israel", "India",
"British Indian Ocean Territory", "Iraq",
"Iran, Islamic Republic of", "Iceland", "Italy", "Jamaica",
"Jordan", "Japan", "Kenya", "Kyrgyzstan", "Cambodia", "Kiribati",
"Comoros", "Saint Kitts and Nevis",
"Korea, Democratic People's Republic of", "Korea, Republic of",
"Kuwait", "Cayman Islands", "Kazakhstan",
"Lao People's Democratic Republic", "Lebanon", "Saint Lucia",
"Liechtenstein", "Sri Lanka", "Liberia", "Lesotho", "Lithuania",
"Luxembourg", "Latvia", "Libya", "Morocco", "Monaco",
"Moldova, Republic of", "Madagascar", "Marshall Islands",
"Macedonia", "Mali", "Myanmar", "Mongolia", "Macau",
"Northern Mariana Islands", "Martinique", "Mauritania",
"Montserrat", "Malta", "Mauritius", "Maldives", "Malawi", "Mexico",
"Malaysia", "Mozambique", "Namibia", "New Caledonia", "Niger",
"Norfolk Island", "Nigeria", "Nicaragua", "Netherlands", "Norway",
"Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama", "Peru",
"French Polynesia", "Papua New Guinea", "Philippines", "Pakistan",
"Poland", "Saint Pierre and Miquelon", "Pitcairn Islands",
"Puerto Rico", "Palestinian Territory", "Portugal", "Palau",
"Paraguay", "Qatar", "Reunion", "Romania", "Russian Federation",
"Rwanda", "Saudi Arabia", "Solomon Islands", "Seychelles", "Sudan",
"Sweden", "Singapore", "Saint Helena", "Slovenia",
"Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino",
"Senegal", "Somalia", "Suriname", "Sao Tome and Principe",
"El Salvador", "Syrian Arab Republic", "Swaziland",
"Turks and Caicos Islands", "Chad", "French Southern Territories",
"Togo", "Thailand", "Tajikistan", "Tokelau", "Turkmenistan",
"Tunisia", "Tonga", "Timor-Leste", "Turkey", "Trinidad and Tobago",
"Tuvalu", "Taiwan", "Tanzania, United Republic of", "Ukraine",
"Uganda", "United States Minor Outlying Islands", "United States",
"Uruguay", "Uzbekistan", "Holy See (Vatican City State)",
"Saint Vincent and the Grenadines", "Venezuela",
"Virgin Islands, British", "Virgin Islands, U.S.", "Vietnam",
"Vanuatu", "Wallis and Futuna", "Samoa", "Yemen", "Mayotte",
"Serbia", "South Africa", "Zambia", "Montenegro", "Zimbabwe",
"Anonymous Proxy", "Satellite Provider", "Other", "Aland Islands",
"Guernsey", "Isle of Man", "Jersey", "Saint Barthelemy",
"Saint Martin", "Bonaire, Saint Eustatius and Saba", "South Sudan",
"Other" };
/* init the hashmap once at startup time */
static {
if (countryCode.length != countryName.length) {
throw new AssertionError("countryCode.length!=countryName.length");
}
}
/**
* Create a new lookup service using the specified database file.
*
* @param databaseFile
* String representation of the database file.
* @throws IOException
* if an error occured creating the lookup service from the
* database file.
*/
public LookupService(String databaseFile) throws IOException {
this(new File(databaseFile));
}
/**
* Create a new lookup service using the specified database file.
*
* @param databaseFile
* the database file.
* @throws IOException
* if an error occured creating the lookup service from the
* database file.
*/
public LookupService(File databaseFile) throws IOException {
this.databaseFile = databaseFile;
file = new RandomAccessFile(databaseFile, "r");
init();
}
/**
* Create a new lookup service using the specified database file.
*
* @param databaseFile
* String representation of the database file.
* @param options
* database flags to use when opening the database GEOIP_STANDARD
* read database from disk GEOIP_MEMORY_CACHE cache the database
* in RAM and read it from RAM
* @throws IOException
* if an error occured creating the lookup service from the
* database file.
*/
public LookupService(String databaseFile, int options) throws IOException {
this(new File(databaseFile), options);
}
/**
* Create a new lookup service using the specified database file.
*
* @param databaseFile
* the database file.
* @param options
* database flags to use when opening the database GEOIP_STANDARD
* read database from disk GEOIP_MEMORY_CACHE cache the database
* in RAM and read it from RAM
* @throws IOException
* if an error occured creating the lookup service from the
* database file.
*/
public LookupService(File databaseFile, int options) throws IOException {
this.databaseFile = databaseFile;
file = new RandomAccessFile(databaseFile, "r");
dboptions = options;
init();
}
/**
* Reads meta-data from the database file.
*
* @throws IOException
* if an error occurs reading from the database file.
*/
private synchronized void init() throws IOException {
byte[] delim = new byte[3];
byte[] buf = new byte[SEGMENT_RECORD_LENGTH];
if (file == null) {
return;
}
if ((dboptions & GEOIP_CHECK_CACHE) != 0) {
mtime = databaseFile.lastModified();
}
file.seek(file.length() - 3);
for (int i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
file.readFully(delim);
if (delim[0] == -1 && delim[1] == -1 && delim[2] == -1) {
databaseType = file.readByte();
if (databaseType >= 106) {
// Backward compatibility with databases from April 2003 and
// earlier
databaseType -= 105;
}
// Determine the database type.
if (databaseType == DatabaseInfo.REGION_EDITION_REV0) {
databaseSegments = new int[1];
databaseSegments[0] = STATE_BEGIN_REV0;
recordLength = STANDARD_RECORD_LENGTH;
} else if (databaseType == DatabaseInfo.REGION_EDITION_REV1) {
databaseSegments = new int[1];
databaseSegments[0] = STATE_BEGIN_REV1;
recordLength = STANDARD_RECORD_LENGTH;
} else if (databaseType == DatabaseInfo.CITY_EDITION_REV0
|| databaseType == DatabaseInfo.CITY_EDITION_REV1
|| databaseType == DatabaseInfo.ORG_EDITION
|| databaseType == DatabaseInfo.ORG_EDITION_V6
|| databaseType == DatabaseInfo.ISP_EDITION
|| databaseType == DatabaseInfo.ISP_EDITION_V6
|| databaseType == DatabaseInfo.DOMAIN_EDITION
|| databaseType == DatabaseInfo.DOMAIN_EDITION_V6
|| databaseType == DatabaseInfo.ASNUM_EDITION
|| databaseType == DatabaseInfo.ASNUM_EDITION_V6
|| databaseType == DatabaseInfo.NETSPEED_EDITION_REV1
|| databaseType == DatabaseInfo.NETSPEED_EDITION_REV1_V6
|| databaseType == DatabaseInfo.CITY_EDITION_REV0_V6
|| databaseType == DatabaseInfo.CITY_EDITION_REV1_V6) {
databaseSegments = new int[1];
databaseSegments[0] = 0;
if (databaseType == DatabaseInfo.CITY_EDITION_REV0
|| databaseType == DatabaseInfo.CITY_EDITION_REV1
|| databaseType == DatabaseInfo.ASNUM_EDITION_V6
|| databaseType == DatabaseInfo.NETSPEED_EDITION_REV1
|| databaseType == DatabaseInfo.NETSPEED_EDITION_REV1_V6
|| databaseType == DatabaseInfo.CITY_EDITION_REV0_V6
|| databaseType == DatabaseInfo.CITY_EDITION_REV1_V6
|| databaseType == DatabaseInfo.ASNUM_EDITION) {
recordLength = STANDARD_RECORD_LENGTH;
} else {
recordLength = ORG_RECORD_LENGTH;
}
file.readFully(buf);
for (int j = 0; j < SEGMENT_RECORD_LENGTH; j++) {
databaseSegments[0] += (unsignedByteToInt(buf[j]) << (j * 8));
}
}
break;
} else {
file.seek(file.getFilePointer() - 4);
}
}
if ((databaseType == DatabaseInfo.COUNTRY_EDITION)
|| (databaseType == DatabaseInfo.COUNTRY_EDITION_V6)
|| (databaseType == DatabaseInfo.PROXY_EDITION)
|| (databaseType == DatabaseInfo.NETSPEED_EDITION)) {
databaseSegments = new int[1];
databaseSegments[0] = COUNTRY_BEGIN;
recordLength = STANDARD_RECORD_LENGTH;
}
if ((dboptions & GEOIP_MEMORY_CACHE) == 1) {
int l = (int) file.length();
dbbuffer = new byte[l];
file.seek(0);
file.readFully(dbbuffer, 0, l);
databaseInfo = getDatabaseInfo();
file.close();
}
if ((dboptions & GEOIP_INDEX_CACHE) != 0) {
int l = databaseSegments[0] * recordLength * 2;
index_cache = new byte[l];
file.seek(0);
file.readFully(index_cache, 0, l);
} else {
index_cache = null;
}
}
/**
* Closes the lookup service.
*/
public synchronized void close() {
try {
if (file != null) {
file.close();
}
file = null;
} catch (IOException e) {
// Here for backward compatibility.
}
}
/**
* @return The list of all known country names
*/
public List<String> getAllCountryNames() {
return Arrays.asList(Arrays.copyOf(countryName, countryName.length));
}
/**
* @return The list of all known country codes
*/
public List<String> getAllCountryCodes() {
return Arrays.asList(Arrays.copyOf(countryCode, countryCode.length));
}
/**
* Returns the country the IP address is in.
*
* @param ipAddress
* String version of an IPv6 address, i.e. "::127.0.0.1"
* @return the country the IP address is from.
*/
public Country getCountryV6(String ipAddress) {
InetAddress addr;
try {
addr = InetAddress.getByName(ipAddress);
} catch (UnknownHostException e) {
return UNKNOWN_COUNTRY;
}
return getCountryV6(addr);
}
/**
* Returns the country the IP address is in.
*
* @param ipAddress
* String version of an IP address, i.e. "127.0.0.1"
* @return the country the IP address is from.
*/
public Country getCountry(String ipAddress) {
InetAddress addr;
try {
addr = InetAddress.getByName(ipAddress);
} catch (UnknownHostException e) {
return UNKNOWN_COUNTRY;
}
return getCountry(addr);
}
/**
* Returns the country the IP address is in.
*
* @param ipAddress
* the IP address.
* @return the country the IP address is from.
*/
public synchronized Country getCountry(InetAddress ipAddress) {
return getCountry(bytesToLong(ipAddress.getAddress()));
}
/**
* Returns the country the IP address is in.
*
* @param addr
* the IP address as Inet6Address.
* @return the country the IP address is from.
*/
public synchronized Country getCountryV6(InetAddress addr) {
if (file == null && (dboptions & GEOIP_MEMORY_CACHE) == 0) {
throw new IllegalStateException("Database has been closed.");
}
int ret = seekCountryV6(addr) - COUNTRY_BEGIN;
if (ret == 0) {
return UNKNOWN_COUNTRY;
} else {
return new Country(countryCode[ret], countryName[ret]);
}
}
/**
* Returns the country the IP address is in.
*
* @param ipAddress
* the IP address in long format.
* @return the country the IP address is from.
*/
public synchronized Country getCountry(long ipAddress) {
if (file == null && (dboptions & GEOIP_MEMORY_CACHE) == 0) {
throw new IllegalStateException("Database has been closed.");
}
int ret = seekCountry(ipAddress) - COUNTRY_BEGIN;
if (ret == 0) {
return UNKNOWN_COUNTRY;
} else {
return new Country(countryCode[ret], countryName[ret]);
}
}
public int getID(String ipAddress) {
InetAddress addr;
try {
addr = InetAddress.getByName(ipAddress);
} catch (UnknownHostException e) {
return 0;
}
return getID(bytesToLong(addr.getAddress()));
}
public int getID(InetAddress ipAddress) {
return getID(bytesToLong(ipAddress.getAddress()));
}
public synchronized int getID(long ipAddress) {
if (file == null && (dboptions & GEOIP_MEMORY_CACHE) == 0) {
throw new IllegalStateException("Database has been closed.");
}
return seekCountry(ipAddress) - databaseSegments[0];
}
public int last_netmask() {
return last_netmask;
}
public void netmask(int nm) {
last_netmask = nm;
}
/**
* Returns information about the database.
*
* @return database info.
*/
public synchronized DatabaseInfo getDatabaseInfo() {
if (databaseInfo != null) {
return databaseInfo;
}
try {
_check_mtime();
boolean hasStructureInfo = false;
byte[] delim = new byte[3];
// Advance to part of file where database info is stored.
file.seek(file.length() - 3);
for (int i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
int read = file.read(delim);
if (read == 3 && (delim[0] & 0xFF) == 255
&& (delim[1] & 0xFF) == 255 && (delim[2] & 0xFF) == 255) {
hasStructureInfo = true;
break;
}
file.seek(file.getFilePointer() - 4);
}
if (hasStructureInfo) {
file.seek(file.getFilePointer() - 6);
} else {
// No structure info, must be pre Sep 2002 database, go back to
// end.
file.seek(file.length() - 3);
}
// Find the database info string.
for (int i = 0; i < DATABASE_INFO_MAX_SIZE; i++) {
file.readFully(delim);
if (delim[0] == 0 && delim[1] == 0 && delim[2] == 0) {
byte[] dbInfo = new byte[i];
file.readFully(dbInfo);
// Create the database info object using the string.
databaseInfo = new DatabaseInfo(new String(dbInfo, charset));
return databaseInfo;
}
file.seek(file.getFilePointer() - 4);
}
} catch (IOException e) {
throw new InvalidDatabaseException("Error reading database info", e);
}
return new DatabaseInfo("");
}
synchronized void _check_mtime() {
try {
if ((dboptions & GEOIP_CHECK_CACHE) != 0) {
long t = databaseFile.lastModified();
if (t != mtime) {
/* GeoIP Database file updated */
/* refresh filehandle */
close();
file = new RandomAccessFile(databaseFile, "r");
databaseInfo = null;
init();
}
}
} catch (IOException e) {
throw new InvalidDatabaseException("Database not found", e);
}
}
// for GeoIP City only
public Location getLocationV6(String str) {
InetAddress addr;
try {
addr = InetAddress.getByName(str);
} catch (UnknownHostException e) {
return null;
}
return getLocationV6(addr);
}
// for GeoIP City only
public Location getLocation(InetAddress addr) {
return getLocation(bytesToLong(addr.getAddress()));
}
// for GeoIP City only
public Location getLocation(String str) {
InetAddress addr;
try {
addr = InetAddress.getByName(str);
} catch (UnknownHostException e) {
return null;
}
return getLocation(addr);
}
public synchronized Region getRegion(String str) {
InetAddress addr;
try {
addr = InetAddress.getByName(str);
} catch (UnknownHostException e) {
return null;
}
return getRegion(bytesToLong(addr.getAddress()));
}
public synchronized Region getRegion(InetAddress addr) {
return getRegion(bytesToLong(addr.getAddress()));
}
public synchronized Region getRegion(long ipnum) {
Region record = new Region();
int seek_region;
if (databaseType == DatabaseInfo.REGION_EDITION_REV0) {
seek_region = seekCountry(ipnum) - STATE_BEGIN_REV0;
char[] ch = new char[2];
if (seek_region >= 1000) {
record.countryCode = "US";
record.countryName = "United States";
ch[0] = (char) (((seek_region - 1000) / 26) + 65);
ch[1] = (char) (((seek_region - 1000) % 26) + 65);
record.region = new String(ch);
} else {
record.countryCode = countryCode[seek_region];
record.countryName = countryName[seek_region];
record.region = "";
}
} else if (databaseType == DatabaseInfo.REGION_EDITION_REV1) {
seek_region = seekCountry(ipnum) - STATE_BEGIN_REV1;
char[] ch = new char[2];
if (seek_region < US_OFFSET) {
record.countryCode = "";
record.countryName = "";
record.region = "";
} else if (seek_region < CANADA_OFFSET) {
record.countryCode = "US";
record.countryName = "United States";
ch[0] = (char) (((seek_region - US_OFFSET) / 26) + 65);
ch[1] = (char) (((seek_region - US_OFFSET) % 26) + 65);
record.region = new String(ch);
} else if (seek_region < WORLD_OFFSET) {
record.countryCode = "CA";
record.countryName = "Canada";
ch[0] = (char) (((seek_region - CANADA_OFFSET) / 26) + 65);
ch[1] = (char) (((seek_region - CANADA_OFFSET) % 26) + 65);
record.region = new String(ch);
} else {
record.countryCode = countryCode[(seek_region - WORLD_OFFSET)
/ FIPS_RANGE];
record.countryName = countryName[(seek_region - WORLD_OFFSET)
/ FIPS_RANGE];
record.region = "";
}
}
return record;
}
public synchronized Location getLocationV6(InetAddress addr) {
int seek_country;
try {
seek_country = seekCountryV6(addr);
return readCityRecord(seek_country);
} catch (IOException e) {
throw new InvalidDatabaseException("Error while seting up segments", e);
}
}
public synchronized Location getLocation(long ipnum) {
int seek_country;
try {
seek_country = seekCountry(ipnum);
return readCityRecord(seek_country);
} catch (IOException e) {
throw new InvalidDatabaseException("Error while seting up segments", e);
}
}
private Location readCityRecord(int seekCountry) throws IOException {
if (seekCountry == databaseSegments[0]) {
return null;
}
ByteBuffer buffer = readRecordBuf(seekCountry, FULL_RECORD_LENGTH);
Location record = new Location();
int country = unsignedByteToInt(buffer.get());
// get country
record.countryCode = countryCode[country];
record.countryName = countryName[country];
record.region = readString(buffer);
record.city = readString(buffer);
record.postalCode = readString(buffer);
record.latitude = readAngle(buffer);
record.longitude = readAngle(buffer);
if (databaseType == DatabaseInfo.CITY_EDITION_REV1) {
// get DMA code
if ("US".equals(record.countryCode)) {
int metroarea_combo = readMetroAreaCombo(buffer);
record.metro_code = record.dma_code = metroarea_combo / 1000;
record.area_code = metroarea_combo % 1000;
}
}
return record;
}
private ByteBuffer readRecordBuf(int seek, int maxLength) throws IOException {
int recordPointer = seek + (2 * recordLength - 1)
* databaseSegments[0];
ByteBuffer buffer;
if ((dboptions & GEOIP_MEMORY_CACHE) == 1) {
buffer = ByteBuffer.wrap(dbbuffer, recordPointer, Math
.min(dbbuffer.length - recordPointer, maxLength));
} else {
byte[] recordBuf = new byte[maxLength];
// read from disk
file.seek(recordPointer);
file.read(recordBuf);
buffer = ByteBuffer.wrap(recordBuf);
}
return buffer;
}
private String readString(ByteBuffer buffer) throws CharacterCodingException {
int start = buffer.position();
int oldLimit = buffer.limit();
while (buffer.hasRemaining() && buffer.get() != 0) {}
int end = buffer.position() - 1;
String str = null;
if (end > start) {
buffer.position(start);
buffer.limit(end);
str = charsetDecoder.decode(buffer).toString();
buffer.limit(oldLimit);
}
buffer.position(end + 1);
return str;
}
private static float readAngle(ByteBuffer buffer) {
if (buffer.remaining() < 3) {
throw new InvalidDatabaseException("Unexpected end of data record when reading angle");
}
double num = 0;
for (int j = 0; j < 3; j++) {
num += unsignedByteToInt(buffer.get()) << (j * 8);
}
return (float) num / 10000 - 180;
}
private static int readMetroAreaCombo(ByteBuffer buffer) {
if (buffer.remaining() < 3) {
throw new InvalidDatabaseException("Unexpected end of data record when reading metro area");
}
int metroareaCombo = 0;
for (int j = 0; j < 3; j++) {
metroareaCombo += unsignedByteToInt(buffer.get()) << (j * 8);
}
return metroareaCombo;
}
public String getOrg(InetAddress addr) {
return getOrg(bytesToLong(addr.getAddress()));
}
public String getOrg(String str) {
InetAddress addr;
try {
addr = InetAddress.getByName(str);
} catch (UnknownHostException e) {
return null;
}
return getOrg(addr);
}
// GeoIP Organization and ISP Edition methods
public synchronized String getOrg(long ipnum) {
try {
int seekOrg = seekCountry(ipnum);
return readOrgRecord(seekOrg);
} catch (IOException e) {
throw new InvalidDatabaseException("Error while reading org", e);
}
}
public String getOrgV6(String str) {
InetAddress addr;
try {
addr = InetAddress.getByName(str);
} catch (UnknownHostException e) {
return null;
}
return getOrgV6(addr);
}
// GeoIP Organization and ISP Edition methods
public synchronized String getOrgV6(InetAddress addr) {
try {
int seekOrg = seekCountryV6(addr);
return readOrgRecord(seekOrg);
} catch (IOException e) {
throw new InvalidDatabaseException("Error while reading org", e);
}
}
private String readOrgRecord(int seekOrg) throws IOException {
if (seekOrg == databaseSegments[0]) {
return null;
}
ByteBuffer buf = readRecordBuf(seekOrg, MAX_ORG_RECORD_LENGTH);
return readString(buf);
}
/**
* Finds the country index value given an IPv6 address.
*
* @param addr
* the ip address to find in long format.
* @return the country index.
*/
private synchronized int seekCountryV6(InetAddress addr) {
byte[] v6vec = addr.getAddress();
if (v6vec.length == 4) {
// sometimes java returns an ipv4 address for IPv6 input
// we have to work around that feature
// It happens for ::ffff:24.24.24.24
byte[] t = new byte[16];
System.arraycopy(v6vec, 0, t, 12, 4);
v6vec = t;
}
byte[] buf = new byte[2 * MAX_RECORD_LENGTH];
int[] x = new int[2];
int offset = 0;
_check_mtime();
for (int depth = 127; depth >= 0; depth--) {
readNode(buf, x, offset);
int bnum = 127 - depth;
int idx = bnum >> 3;
int b_mask = 1 << (bnum & 7 ^ 7);
if ((v6vec[idx] & b_mask) > 0) {
if (x[1] >= databaseSegments[0]) {
last_netmask = 128 - depth;
return x[1];
}
offset = x[1];
} else {
if (x[0] >= databaseSegments[0]) {
last_netmask = 128 - depth;
return x[0];
}
offset = x[0];
}
}
throw new InvalidDatabaseException("Error seeking country while searching for "
+ addr.getHostAddress());
}
/**
* Finds the country index value given an IP address.
*
* @param ipAddress
* the ip address to find in long format.
* @return the country index.
*/
private synchronized int seekCountry(long ipAddress) {
byte[] buf = new byte[2 * MAX_RECORD_LENGTH];
int[] x = new int[2];
int offset = 0;
_check_mtime();
for (int depth = 31; depth >= 0; depth--) {
readNode(buf, x, offset);
if ((ipAddress & (1 << depth)) > 0) {
if (x[1] >= databaseSegments[0]) {
last_netmask = 32 - depth;
return x[1];
}
offset = x[1];
} else {
if (x[0] >= databaseSegments[0]) {
last_netmask = 32 - depth;
return x[0];
}
offset = x[0];
}
}
throw new InvalidDatabaseException("Error seeking country while searching for "
+ ipAddress);
}
private void readNode(byte[] buf, int[] x, int offset) {
if ((dboptions & GEOIP_MEMORY_CACHE) == 1) {
// read from memory
System.arraycopy(dbbuffer, (2 * recordLength * offset), buf, 0, 2 * recordLength);
} else if ((dboptions & GEOIP_INDEX_CACHE) != 0) {
// read from index cache
System.arraycopy(index_cache, (2 * recordLength * offset), buf, 0, 2 * recordLength);
} else {
// read from disk
try {
file.seek(2 * recordLength * offset);
file.read(buf);
} catch (IOException e) {
throw new InvalidDatabaseException("Error seeking in database", e);
}
}
for (int i = 0; i < 2; i++) {
x[i] = 0;
for (int j = 0; j < recordLength; j++) {
int y = buf[i * recordLength + j];
if (y < 0) {
y += 256;
}
x[i] += (y << (j * 8));
}
}
}
/**
* Returns the long version of an IP address given an InetAddress object.
*
* @param address
* the InetAddress.
* @return the long form of the IP address.
*/
private static long bytesToLong(byte[] address) {
long ipnum = 0;
for (int i = 0; i < 4; ++i) {
long y = address[i];
if (y < 0) {
y += 256;
}
ipnum += y << ((3 - i) * 8);
}
return ipnum;
}
private static int unsignedByteToInt(byte b) {
return (int) b & 0xFF;
}
}

View File

@@ -0,0 +1,7 @@
package com.maxmind.geoip;
public class Region {
public String countryCode;
public String countryName;
public String region;
}

View File

@@ -0,0 +1,15 @@
<html>
<body>
<p>
This is geoip-api-java release 1.3.1 2016-02-08
retrieved from <a href="https://github.com/maxmind/geoip-api-java">github</a>.
It is used only on Debian-based systems where the geoip-database package
is installed, with the files in /usr/share/GeoIP/ .
See net.i2p.router.transport.GeoIP.
</p><p>
Unmodified, except that
regionName.java and timeZone.java are omitted.
LGPL v2.1.
</p>
</body>
</html>

View File

@@ -16,6 +16,9 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import com.maxmind.geoip.InvalidDatabaseException;
import com.maxmind.geoip.LookupService;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
@@ -24,6 +27,7 @@ import net.i2p.router.RouterContext;
import net.i2p.util.Addresses;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.Log;
import net.i2p.util.SystemVersion;
/**
* Manage geoip lookup in a file with the Tor geoip format.
@@ -56,6 +60,20 @@ public class GeoIP {
private final AtomicBoolean _lock;
private int _lookupRunCount;
static final String PROP_GEOIP_ENABLED = "routerconsole.geoip.enable";
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 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();
/** maxmind API */
private static final String UNKNOWN_COUNTRY_CODE = "--";
/**
* @param context RouterContext in production, I2PAppContext for testing only
*/
@@ -71,13 +89,6 @@ public class GeoIP {
_lock = new AtomicBoolean();
readCountryFile();
}
static final String PROP_GEOIP_ENABLED = "routerconsole.geoip.enable";
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 COUNTRY_FILE_DEFAULT = "countries.txt";
public static final String PROP_IP_COUNTRY = "i2np.lastCountry";
/**
* @since 0.9.3
@@ -145,12 +156,39 @@ public class GeoIP {
_pendingSearch.clear();
if (search.length > 0) {
Arrays.sort(search);
String[] countries = readGeoIPFile(search);
for (int i = 0; i < countries.length; i++) {
if (countries[i] != null)
_IPToCountry.put(search[i], countries[i]);
else
_notFound.add(search[i]);
File f = new File(_context.getProperty(PROP_DEBIAN_GEOIP, DEBIAN_GEOIP_FILE));
if (ENABLE_DEBIAN && f.exists()) {
// Maxmind 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();
// 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);
} else {
_notFound.add(search[i]);
}
}
} catch (IOException ioe) {
_log.error("GeoIP failure", ioe);
} catch (InvalidDatabaseException ide) {
_log.error("GeoIP failure", ide);
} finally {
if (ls != null) ls.close();
}
} else {
// Tor-style database
String[] countries = readGeoIPFile(search);
for (int i = 0; i < countries.length; i++) {
if (countries[i] != null)
_IPToCountry.put(search[i], countries[i]);
else
_notFound.add(search[i]);
}
}
}
// IPv6
@@ -158,12 +196,40 @@ public class GeoIP {
_pendingIPv6Search.clear();
if (search.length > 0) {
Arrays.sort(search);
String[] countries = GeoIPv6.readGeoIPFile(_context, search, _codeCache);
for (int i = 0; i < countries.length; i++) {
if (countries[i] != null)
_IPToCountry.put(search[i], countries[i]);
else
_notFound.add(search[i]);
File f = new File(_context.getProperty(PROP_DEBIAN_GEOIPV6, DEBIAN_GEOIPV6_FILE));
if (ENABLE_DEBIAN && f.exists()) {
// Maxmind 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();
String ipv6 = toV6(ip);
// returns upper case or "--"
String uc = ls.getCountryV6(ipv6).getCode();
if (!uc.equals(UNKNOWN_COUNTRY_CODE)) {
String cached = _codeCache.get(uc.toLowerCase(Locale.US));
_IPToCountry.put(search[i], cached);
} else {
_notFound.add(search[i]);
}
}
} catch (IOException ioe) {
_log.error("GeoIP failure", ioe);
} catch (InvalidDatabaseException ide) {
_log.error("GeoIP failure", ide);
} finally {
if (ls != null) ls.close();
}
} else {
// Tor-style database
String[] countries = GeoIPv6.readGeoIPFile(_context, search, _codeCache);
for (int i = 0; i < countries.length; i++) {
if (countries[i] != null)
_IPToCountry.put(search[i], countries[i]);
else
_notFound.add(search[i]);
}
}
}
} finally {
@@ -404,6 +470,20 @@ public class GeoIP {
}
}
/**
* @return e.g. aabb:ccdd:eeff:1122::
* @since 0.9.26 for maxmind
*/
private static String toV6(long ip) {
StringBuilder buf = new StringBuilder(21);
for (int i = 0; i < 4; i++) {
buf.append(Long.toHexString((ip >> ((3-i)*16)) & 0xffff));
buf.append(':');
}
buf.append(':');
return buf.toString();
}
/**
* Get the country for a country code
* @param code two-letter lower case code