forked from I2P_Developers/i2p.i2p
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:
@@ -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:
|
||||
|
13
build.xml
13
build.xml
@@ -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
4
debian/control
vendored
@@ -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
2
debian/repack.sh
vendored
@@ -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..."
|
||||
|
63
router/java/src/com/maxmind/geoip/Country.java
Normal file
63
router/java/src/com/maxmind/geoip/Country.java
Normal 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;
|
||||
}
|
||||
}
|
128
router/java/src/com/maxmind/geoip/DatabaseInfo.java
Normal file
128
router/java/src/com/maxmind/geoip/DatabaseInfo.java
Normal 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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
62
router/java/src/com/maxmind/geoip/Location.java
Normal file
62
router/java/src/com/maxmind/geoip/Location.java
Normal 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));
|
||||
}
|
||||
}
|
988
router/java/src/com/maxmind/geoip/LookupService.java
Normal file
988
router/java/src/com/maxmind/geoip/LookupService.java
Normal 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("c:\\geoip.dat");
|
||||
* // Assume we have a String ipAddress (in dot-decimal form).
|
||||
* Country country = lookupService.getCountry(ipAddress);
|
||||
* System.out.println("The country is: " + country.getName());
|
||||
* System.out.println("The country code is: " + 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("/GeoIP.dat").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;
|
||||
}
|
||||
}
|
7
router/java/src/com/maxmind/geoip/Region.java
Normal file
7
router/java/src/com/maxmind/geoip/Region.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package com.maxmind.geoip;
|
||||
|
||||
public class Region {
|
||||
public String countryCode;
|
||||
public String countryName;
|
||||
public String region;
|
||||
}
|
15
router/java/src/com/maxmind/geoip/package.html
Normal file
15
router/java/src/com/maxmind/geoip/package.html
Normal 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>
|
@@ -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
|
||||
|
Reference in New Issue
Block a user