Skip to content

Instantly share code, notes, and snippets.

@diega
Created December 7, 2014 20:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save diega/b6b6cff7b1104c8f87ce to your computer and use it in GitHub Desktop.
Save diega/b6b6cff7b1104c8f87ce to your computer and use it in GitHub Desktop.
Reimplementation of Maxmind GeoIP Legacy for supporting full in memory lookups
package net.taringa.zeti.maxmind;
import com.maxmind.geoip.Country;
import com.maxmind.geoip.DatabaseInfo;
import com.maxmind.geoip.Location;
import com.maxmind.geoip.Region;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import static java.lang.System.arraycopy;
public class InMemoryLookupService {
/**
* Information about the database.
*/
private DatabaseInfo databaseInfo = null;
/**
* The database type. Default is the country edition.
*/
byte databaseType = DatabaseInfo.COUNTRY_EDITION;
int databaseSegments[];
int recordLength;
byte dbbuffer[];
int last_netmask;
private final static int US_OFFSET = 1;
private final static int CANADA_OFFSET = 677;
private final static int WORLD_OFFSET = 1353;
private final static int FIPS_RANGE = 360;
private final static int COUNTRY_BEGIN = 16776960;
private final static int STATE_BEGIN_REV0 = 16700000;
private final static int STATE_BEGIN_REV1 = 16000000;
private final static int STRUCTURE_INFO_MAX_SIZE = 20;
private final static int DATABASE_INFO_MAX_SIZE = 100;
private final static int SEGMENT_RECORD_LENGTH = 3;
private final static int STANDARD_RECORD_LENGTH = 3;
private final static int ORG_RECORD_LENGTH = 4;
private final static int MAX_RECORD_LENGTH = 4;
private final static int MAX_ORG_RECORD_LENGTH = 300;
private final static 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");
}
public InMemoryLookupService(byte[] dbbuffer) {
this.dbbuffer = dbbuffer;
init();
}
/**
* Reads meta-data from the database file.
*
* @throws java.io.IOException
* if an error occurs reading from the database file.
*/
private void init() {
int i, j;
int delimSize = 3;
int bufSize = SEGMENT_RECORD_LENGTH;
byte[] delim;
byte[] buf;
int structureInfoBeginIndex = dbbuffer.length - 3;
for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
delim = Arrays.copyOfRange(dbbuffer, structureInfoBeginIndex, structureInfoBeginIndex + delimSize);
if (delim[0] == -1 && delim[1] == -1 && delim[2] == -1) {
structureInfoBeginIndex += delimSize;
databaseType = dbbuffer[structureInfoBeginIndex];
structureInfoBeginIndex++;
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;
}
buf = Arrays.copyOfRange(dbbuffer, structureInfoBeginIndex, structureInfoBeginIndex + bufSize);
structureInfoBeginIndex += bufSize;
for (j = 0; j < SEGMENT_RECORD_LENGTH; j++) {
databaseSegments[0] += (unsignedByteToInt(buf[j]) << (j * 8));
}
}
break;
} else {
structureInfoBeginIndex = structureInfoBeginIndex - 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;
}
databaseInfo = this.getDatabaseInfo();
}
/**
* 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(bytesToLong(addr.getAddress()));
}
/**
* 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 Country getCountryV6(InetAddress addr) {
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 Country getCountry(long ipAddress) {
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) {
int ret = seekCountry(ipAddress) - databaseSegments[0];
return ret;
}
public int last_netmask() {
return this.last_netmask;
}
public void netmask(int nm) {
this.last_netmask = nm;
}
/**
* Returns information about the database.
*
* @return database info.
*/
public synchronized DatabaseInfo getDatabaseInfo() {
if (databaseInfo != null) {
return databaseInfo;
}
try {
boolean hasStructureInfo = false;
int delimSize = 3;
byte[] delim;
int structureInfoBeginIndex = dbbuffer.length - delimSize;
// Advance to part of file where database info is stored.
for (int i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
delim = Arrays.copyOfRange(dbbuffer, structureInfoBeginIndex, structureInfoBeginIndex + delimSize);
boolean fullStructureRead = dbbuffer.length >= structureInfoBeginIndex + delimSize;
if (fullStructureRead) {
structureInfoBeginIndex += delimSize;
} else {
structureInfoBeginIndex += (structureInfoBeginIndex + delimSize) - dbbuffer.length;
}
if (fullStructureRead && (delim[0] & 0xFF) == 255
&& (delim[1] & 0xFF) == 255 && (delim[2] & 0xFF) == 255) {
hasStructureInfo = true;
break;
}
structureInfoBeginIndex -= delimSize + 1;
}
if (hasStructureInfo) {
structureInfoBeginIndex -= 6;
} else {
// No structure info, must be pre Sep 2002 database, go back to
// end.
structureInfoBeginIndex -= 3;
}
// Find the database info string.
for (int i = 0; i < DATABASE_INFO_MAX_SIZE; i++) {
delim = Arrays.copyOfRange(dbbuffer, structureInfoBeginIndex, structureInfoBeginIndex + delimSize);
structureInfoBeginIndex += delimSize;
if (delim[0] == 0 && delim[1] == 0 && delim[2] == 0) {
byte[] dbInfo = Arrays.copyOfRange(dbbuffer, structureInfoBeginIndex, structureInfoBeginIndex + i);
structureInfoBeginIndex += i;
// Create the database info object using the string.
return new DatabaseInfo(new String(dbInfo));
}
structureInfoBeginIndex -= delimSize + 1;
}
} catch (Exception e) {
e.printStackTrace();
}
return new DatabaseInfo("");
}
// 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(long ipnum) {
Region record = new Region();
int seek_region = 0;
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 record_pointer;
byte record_buf[] = new byte[FULL_RECORD_LENGTH];
int record_buf_offset = 0;
Location record = new Location();
int str_length = 0;
int j, seek_country;
double latitude = 0, longitude = 0;
try {
seek_country = seekCountryV6(addr);
if (seek_country == databaseSegments[0]) {
return null;
}
record_pointer = seek_country + (2 * recordLength - 1)
* databaseSegments[0];
// read from memory
System.arraycopy(dbbuffer, record_pointer, record_buf, 0, Math
.min(dbbuffer.length - record_pointer,
FULL_RECORD_LENGTH));
// get country
record.countryCode = countryCode[unsignedByteToInt(record_buf[0])];
record.countryName = countryName[unsignedByteToInt(record_buf[0])];
record_buf_offset++;
// get region
while (record_buf[record_buf_offset + str_length] != '\0')
str_length++;
if (str_length > 0) {
record.region = new String(record_buf, record_buf_offset,
str_length);
}
record_buf_offset += str_length + 1;
str_length = 0;
// get city
while (record_buf[record_buf_offset + str_length] != '\0')
str_length++;
if (str_length > 0) {
record.city = new String(record_buf, record_buf_offset,
str_length, "ISO-8859-1");
}
record_buf_offset += str_length + 1;
str_length = 0;
// get postal code
while (record_buf[record_buf_offset + str_length] != '\0')
str_length++;
if (str_length > 0) {
record.postalCode = new String(record_buf, record_buf_offset,
str_length);
}
record_buf_offset += str_length + 1;
// get latitude
for (j = 0; j < 3; j++)
latitude += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8));
record.latitude = (float) latitude / 10000 - 180;
record_buf_offset += 3;
// get longitude
for (j = 0; j < 3; j++)
longitude += (unsignedByteToInt(record_buf[record_buf_offset
+ j]) << (j * 8));
record.longitude = (float) longitude / 10000 - 180;
record.dma_code = record.metro_code = 0;
record.area_code = 0;
if (databaseType == DatabaseInfo.CITY_EDITION_REV1) {
// get DMA code
int metroarea_combo = 0;
if ("US".equals(record.countryCode)) {
record_buf_offset += 3;
for (j = 0; j < 3; j++)
metroarea_combo += (unsignedByteToInt(record_buf[record_buf_offset
+ j]) << (j * 8));
record.metro_code = record.dma_code = metroarea_combo / 1000;
record.area_code = metroarea_combo % 1000;
}
}
} catch (IOException e) {
System.err.println("IO Exception while seting up segments");
}
return record;
}
public synchronized Location getLocation(long ipnum) {
int record_pointer;
byte record_buf[] = new byte[FULL_RECORD_LENGTH];
int record_buf_offset = 0;
Location record = new Location();
int str_length = 0;
int j, seek_country;
double latitude = 0, longitude = 0;
try {
seek_country = seekCountry(ipnum);
if (seek_country == databaseSegments[0]) {
return null;
}
record_pointer = seek_country + (2 * recordLength - 1)
* databaseSegments[0];
// read from memory
System.arraycopy(dbbuffer, record_pointer, record_buf, 0, Math
.min(dbbuffer.length - record_pointer,
FULL_RECORD_LENGTH));
// get country
record.countryCode = countryCode[unsignedByteToInt(record_buf[0])];
record.countryName = countryName[unsignedByteToInt(record_buf[0])];
record_buf_offset++;
// get region
while (record_buf[record_buf_offset + str_length] != '\0')
str_length++;
if (str_length > 0) {
record.region = new String(record_buf, record_buf_offset,
str_length);
}
record_buf_offset += str_length + 1;
str_length = 0;
// get city
while (record_buf[record_buf_offset + str_length] != '\0')
str_length++;
if (str_length > 0) {
record.city = new String(record_buf, record_buf_offset,
str_length, "ISO-8859-1");
}
record_buf_offset += str_length + 1;
str_length = 0;
// get postal code
while (record_buf[record_buf_offset + str_length] != '\0')
str_length++;
if (str_length > 0) {
record.postalCode = new String(record_buf, record_buf_offset,
str_length);
}
record_buf_offset += str_length + 1;
// get latitude
for (j = 0; j < 3; j++)
latitude += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8));
record.latitude = (float) latitude / 10000 - 180;
record_buf_offset += 3;
// get longitude
for (j = 0; j < 3; j++)
longitude += (unsignedByteToInt(record_buf[record_buf_offset
+ j]) << (j * 8));
record.longitude = (float) longitude / 10000 - 180;
record.dma_code = record.metro_code = 0;
record.area_code = 0;
if (databaseType == DatabaseInfo.CITY_EDITION_REV1) {
// get DMA code
int metroarea_combo = 0;
if ("US".equals(record.countryCode)) {
record_buf_offset += 3;
for (j = 0; j < 3; j++)
metroarea_combo += (unsignedByteToInt(record_buf[record_buf_offset
+ j]) << (j * 8));
record.metro_code = record.dma_code = metroarea_combo / 1000;
record.area_code = metroarea_combo % 1000;
}
}
} catch (IOException e) {
System.err.println("IO Exception while seting up segments");
}
return record;
}
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) {
int seek_org;
int record_pointer;
byte[] buf = new byte[MAX_ORG_RECORD_LENGTH];
String org_buf;
try {
seek_org = seekCountry(ipnum);
if (seek_org == databaseSegments[0]) {
return null;
}
record_pointer = seek_org + (2 * recordLength - 1)
* databaseSegments[0];
// read from memory
System.arraycopy(dbbuffer, record_pointer, buf, 0, Math
.min(dbbuffer.length - record_pointer,
MAX_ORG_RECORD_LENGTH));
int strLength;
for (strLength = 0; strLength < buf.length && buf[strLength] != 0; strLength++) { }
org_buf = new String(buf, 0, strLength, "ISO-8859-1");
return org_buf;
} catch (IOException e) {
System.out.println("IO Exception");
return null;
}
}
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) {
int seek_org;
int record_pointer;
byte[] buf = new byte[MAX_ORG_RECORD_LENGTH];
String org_buf;
try {
seek_org = seekCountryV6(addr);
if (seek_org == databaseSegments[0]) {
return null;
}
record_pointer = seek_org + (2 * recordLength - 1)
* databaseSegments[0];
// read from memory
System.arraycopy(dbbuffer, record_pointer, buf, 0, Math
.min(dbbuffer.length - record_pointer,
MAX_ORG_RECORD_LENGTH));
int strLength;
for (strLength = 0; strLength < buf.length && buf[strLength] != 0; strLength++) { }
org_buf = new String(buf, 0, strLength, "ISO-8859-1");
return org_buf;
} catch (IOException e) {
System.out.println("IO Exception");
return null;
}
}
/**
* 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];
arraycopy(v6vec, 0, t, 12, 4);
v6vec = t;
}
byte[] buf = new byte[2 * MAX_RECORD_LENGTH];
int[] x = new int[2];
int offset = 0;
for (int depth = 127; depth >= 0; depth--) {
// read from memory
for (int i = 0; i < 2 * MAX_RECORD_LENGTH; i++) {
buf[i] = dbbuffer[(2 * recordLength * offset) + i];
}
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));
}
}
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];
}
}
// shouldn't reach here
System.err.println("Error seeking country while seeking "
+ addr.getHostAddress());
return 0;
}
/**
* 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;
for (int depth = 31; depth >= 0; depth--) {
// read from memory
for (int i = 0; i < 2 * recordLength; i++) {
buf[i] = dbbuffer[(2 * recordLength * offset) + i];
}
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));
}
}
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];
}
}
// shouldn't reach here
System.err.println("Error seeking country while seeking " + ipAddress);
return 0;
}
/**
* 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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment