summaryrefslogtreecommitdiffstats
path: root/libGeoIP/GeoIP.c
diff options
context:
space:
mode:
Diffstat (limited to 'libGeoIP/GeoIP.c')
-rw-r--r--libGeoIP/GeoIP.c2766
1 files changed, 2766 insertions, 0 deletions
diff --git a/libGeoIP/GeoIP.c b/libGeoIP/GeoIP.c
new file mode 100644
index 0000000..587326a
--- /dev/null
+++ b/libGeoIP/GeoIP.c
@@ -0,0 +1,2766 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */
+/* GeoIP.c
+ *
+ * Copyright (C) 2016 MaxMind, Inc.
+ *
+ * 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.1 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
+ * Lesser 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "GeoIP.h"
+#include "GeoIP_internal.h"
+
+static geoipv6_t IPV6_NULL;
+
+#if defined(_WIN32)
+#include <io.h>
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1900 // VS 2015 supports snprintf
+#define snprintf _snprintf
+#endif
+#if _MSC_VER >= 1400 // VS 2005+ deprecates fileno, lseek and read
+#define fileno _fileno
+#define read _read
+#define lseek _lseek
+#endif
+#endif
+#else
+#include <netdb.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#endif /* defined(_WIN32) */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h> /* for fstat */
+#include <sys/types.h> /* for fstat */
+
+#ifdef HAVE_GETTIMEOFDAY
+#include <sys/time.h> /* for gettimeofday */
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h> /* For uint32_t */
+#endif
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+#include "pread.h"
+#endif
+
+#ifdef _UNUSED
+#elif defined(__GNUC__)
+#define _UNUSED __attribute__((unused))
+#else
+#define _UNUSED
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
+
+#define COUNTRY_BEGIN 16776960
+#define LARGE_COUNTRY_BEGIN 16515072
+#define STATE_BEGIN_REV0 16700000
+#define STATE_BEGIN_REV1 16000000
+#define STRUCTURE_INFO_MAX_SIZE 20
+#define DATABASE_INFO_MAX_SIZE 100
+#define MAX_ORG_RECORD_LENGTH 300
+#define US_OFFSET 1
+#define CANADA_OFFSET 677
+#define WORLD_OFFSET 1353
+#define FIPS_RANGE 360
+
+#define DEBUG_MSGF(flags, fmt, ...) \
+ { \
+ if (((flags)&GEOIP_SILENCE) == 0) { \
+ fprintf(stderr, fmt, ##__VA_ARGS__); \
+ } \
+ }
+
+#ifndef HAVE_PREAD
+#define pread(fd, buf, count, offset) \
+ (lseek(fd, offset, SEEK_SET) == offset ? read(fd, buf, count) : -1)
+#endif /* HAVE_PREAD */
+
+const char GeoIP_country_code[256][3] = {
+ "--", "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"};
+
+static const unsigned num_GeoIP_countries =
+ (unsigned)(sizeof(GeoIP_country_code) / sizeof(GeoIP_country_code[0]));
+
+const char GeoIP_country_code3[256][4] = {
+ "--", "AP", "EU", "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM", "CUW",
+ "AGO", "ATA", "ARG", "ASM", "AUT", "AUS", "ABW", "AZE", "BIH", "BRB", "BGD",
+ "BEL", "BFA", "BGR", "BHR", "BDI", "BEN", "BMU", "BRN", "BOL", "BRA", "BHS",
+ "BTN", "BVT", "BWA", "BLR", "BLZ", "CAN", "CCK", "COD", "CAF", "COG", "CHE",
+ "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CRI", "CUB", "CPV", "CXR", "CYP",
+ "CZE", "DEU", "DJI", "DNK", "DMA", "DOM", "DZA", "ECU", "EST", "EGY", "ESH",
+ "ERI", "ESP", "ETH", "FIN", "FJI", "FLK", "FSM", "FRO", "FRA", "SXM", "GAB",
+ "GBR", "GRD", "GEO", "GUF", "GHA", "GIB", "GRL", "GMB", "GIN", "GLP", "GNQ",
+ "GRC", "SGS", "GTM", "GUM", "GNB", "GUY", "HKG", "HMD", "HND", "HRV", "HTI",
+ "HUN", "IDN", "IRL", "ISR", "IND", "IOT", "IRQ", "IRN", "ISL", "ITA", "JAM",
+ "JOR", "JPN", "KEN", "KGZ", "KHM", "KIR", "COM", "KNA", "PRK", "KOR", "KWT",
+ "CYM", "KAZ", "LAO", "LBN", "LCA", "LIE", "LKA", "LBR", "LSO", "LTU", "LUX",
+ "LVA", "LBY", "MAR", "MCO", "MDA", "MDG", "MHL", "MKD", "MLI", "MMR", "MNG",
+ "MAC", "MNP", "MTQ", "MRT", "MSR", "MLT", "MUS", "MDV", "MWI", "MEX", "MYS",
+ "MOZ", "NAM", "NCL", "NER", "NFK", "NGA", "NIC", "NLD", "NOR", "NPL", "NRU",
+ "NIU", "NZL", "OMN", "PAN", "PER", "PYF", "PNG", "PHL", "PAK", "POL", "SPM",
+ "PCN", "PRI", "PSE", "PRT", "PLW", "PRY", "QAT", "REU", "ROU", "RUS", "RWA",
+ "SAU", "SLB", "SYC", "SDN", "SWE", "SGP", "SHN", "SVN", "SJM", "SVK", "SLE",
+ "SMR", "SEN", "SOM", "SUR", "STP", "SLV", "SYR", "SWZ", "TCA", "TCD", "ATF",
+ "TGO", "THA", "TJK", "TKL", "TKM", "TUN", "TON", "TLS", "TUR", "TTO", "TUV",
+ "TWN", "TZA", "UKR", "UGA", "UMI", "USA", "URY", "UZB", "VAT", "VCT", "VEN",
+ "VGB", "VIR", "VNM", "VUT", "WLF", "WSM", "YEM", "MYT", "SRB", "ZAF", "ZMB",
+ "MNE", "ZWE", "A1", "A2", "O1", "ALA", "GGY", "IMN", "JEY", "BLM", "MAF",
+ "BES", "SSD", "O1"};
+
+const char *GeoIP_utf8_country_name[256] = {
+ "N/A",
+ "Asia/Pacific Region",
+ "Europe",
+ "Andorra",
+ "United Arab Emirates",
+ "Afghanistan",
+ "Antigua and Barbuda",
+ "Anguilla",
+ "Albania",
+ "Armenia",
+ "Cura"
+ "\xc3\xa7"
+ "ao",
+ "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"};
+
+const char *GeoIP_country_name[256] = {
+ "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"};
+
+/* Possible continent codes are AF, AS, EU, NA, OC, SA for Africa, Asia, Europe,
+ North America, Oceania
+ and South America. */
+
+const char GeoIP_country_continent[256][3] = {
+ "--", "AS", "EU", "EU", "AS", "AS", "NA", "NA", "EU", "AS", "NA", "AF",
+ "AN", "SA", "OC", "EU", "OC", "NA", "AS", "EU", "NA", "AS", "EU", "AF",
+ "EU", "AS", "AF", "AF", "NA", "AS", "SA", "SA", "NA", "AS", "AN", "AF",
+ "EU", "NA", "NA", "AS", "AF", "AF", "AF", "EU", "AF", "OC", "SA", "AF",
+ "AS", "SA", "NA", "NA", "AF", "AS", "AS", "EU", "EU", "AF", "EU", "NA",
+ "NA", "AF", "SA", "EU", "AF", "AF", "AF", "EU", "AF", "EU", "OC", "SA",
+ "OC", "EU", "EU", "NA", "AF", "EU", "NA", "AS", "SA", "AF", "EU", "NA",
+ "AF", "AF", "NA", "AF", "EU", "AN", "NA", "OC", "AF", "SA", "AS", "AN",
+ "NA", "EU", "NA", "EU", "AS", "EU", "AS", "AS", "AS", "AS", "AS", "EU",
+ "EU", "NA", "AS", "AS", "AF", "AS", "AS", "OC", "AF", "NA", "AS", "AS",
+ "AS", "NA", "AS", "AS", "AS", "NA", "EU", "AS", "AF", "AF", "EU", "EU",
+ "EU", "AF", "AF", "EU", "EU", "AF", "OC", "EU", "AF", "AS", "AS", "AS",
+ "OC", "NA", "AF", "NA", "EU", "AF", "AS", "AF", "NA", "AS", "AF", "AF",
+ "OC", "AF", "OC", "AF", "NA", "EU", "EU", "AS", "OC", "OC", "OC", "AS",
+ "NA", "SA", "OC", "OC", "AS", "AS", "EU", "NA", "OC", "NA", "AS", "EU",
+ "OC", "SA", "AS", "AF", "EU", "EU", "AF", "AS", "OC", "AF", "AF", "EU",
+ "AS", "AF", "EU", "EU", "EU", "AF", "EU", "AF", "AF", "SA", "AF", "NA",
+ "AS", "AF", "NA", "AF", "AN", "AF", "AS", "AS", "OC", "AS", "AF", "OC",
+ "AS", "EU", "NA", "OC", "AS", "AF", "EU", "AF", "OC", "NA", "SA", "AS",
+ "EU", "NA", "SA", "NA", "NA", "AS", "OC", "OC", "OC", "AS", "AF", "EU",
+ "AF", "AF", "EU", "AF", "--", "--", "--", "EU", "EU", "EU", "EU", "NA",
+ "NA", "NA", "AF", "--"};
+
+static const char *get_db_description(int dbtype) {
+ const char *ptr;
+ if (dbtype >= NUM_DB_TYPES || dbtype < 0) {
+ return "Unknown";
+ }
+ ptr = GeoIPDBDescription[dbtype];
+ return ptr == NULL ? "Unknown" : ptr;
+}
+
+geoipv6_t _GeoIP_lookupaddress_v6(const char *host);
+
+#if defined(_WIN32)
+/* http://www.mail-archive.com/users@ipv6.org/msg02107.html */
+static const char *
+_GeoIP_inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
+ if (af == AF_INET) {
+ struct sockaddr_in in;
+ memset(&in, 0, sizeof(in));
+ in.sin_family = AF_INET;
+ memcpy(&in.sin_addr, src, sizeof(struct in_addr));
+ getnameinfo((struct sockaddr *)&in,
+ sizeof(struct sockaddr_in),
+ dst,
+ cnt,
+ NULL,
+ 0,
+ NI_NUMERICHOST);
+ return dst;
+ } else if (af == AF_INET6) {
+ struct sockaddr_in6 in;
+ memset(&in, 0, sizeof(in));
+ in.sin6_family = AF_INET6;
+ memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
+ getnameinfo((struct sockaddr *)&in,
+ sizeof(struct sockaddr_in6),
+ dst,
+ cnt,
+ NULL,
+ 0,
+ NI_NUMERICHOST);
+ return dst;
+ }
+ return NULL;
+}
+
+static int _GeoIP_inet_pton(int af, const char *src, void *dst) {
+ struct addrinfo hints, *res, *ressave;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = af;
+
+ if (getaddrinfo(src, NULL, &hints, &res) != 0) {
+ return -1;
+ }
+
+ ressave = res;
+
+ while (res) {
+ memcpy(dst, res->ai_addr, res->ai_addrlen);
+ res = res->ai_next;
+ }
+
+ freeaddrinfo(ressave);
+ return 0;
+}
+#else
+static int _GeoIP_inet_pton(int af, const char *src, void *dst) {
+ return inet_pton(af, src, dst);
+}
+static const char *
+_GeoIP_inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
+ return inet_ntop(af, src, dst, cnt);
+}
+#endif /* defined(_WIN32) */
+
+int __GEOIP_V6_IS_NULL(geoipv6_t v6) {
+ int i;
+ for (i = 0; i < 16; i++) {
+ if (v6.s6_addr[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void __GEOIP_PREPARE_TEREDO(geoipv6_t *v6) {
+ int i;
+ if ((v6->s6_addr[0]) != 0x20) {
+ return;
+ }
+ if ((v6->s6_addr[1]) != 0x01) {
+ return;
+ }
+ if ((v6->s6_addr[2]) != 0x00) {
+ return;
+ }
+ if ((v6->s6_addr[3]) != 0x00) {
+ return;
+ }
+
+ for (i = 0; i < 12; i++) {
+ v6->s6_addr[i] = 0;
+ }
+ for (; i < 16; i++) {
+ v6->s6_addr[i] ^= 0xff;
+ }
+}
+
+const char *GeoIPDBDescription[NUM_DB_TYPES] = {
+ NULL,
+ "GeoIP Country Edition",
+ "GeoIP City Edition, Rev 1",
+ "GeoIP Region Edition, Rev 1",
+ "GeoIP ISP Edition",
+ "GeoIP Organization Edition",
+ "GeoIP City Edition, Rev 0",
+ "GeoIP Region Edition, Rev 0",
+ "GeoIP Proxy Edition",
+ "GeoIP ASNum Edition",
+ "GeoIP Netspeed Edition",
+ "GeoIP Domain Name Edition",
+ "GeoIP Country V6 Edition",
+ "GeoIP LocationID ASCII Edition",
+ "GeoIP Accuracy Radius Edition",
+ NULL,
+ NULL,
+ "GeoIP Large Country Edition",
+ "GeoIP Large Country V6 Edition",
+ NULL,
+ "GeoIP CCM Edition",
+ "GeoIP ASNum V6 Edition",
+ "GeoIP ISP V6 Edition",
+ "GeoIP Organization V6 Edition",
+ "GeoIP Domain Name V6 Edition",
+ "GeoIP LocationID ASCII V6 Edition",
+ "GeoIP Registrar Edition",
+ "GeoIP Registrar V6 Edition",
+ "GeoIP UserType Edition",
+ "GeoIP UserType V6 Edition",
+ "GeoIP City Edition V6, Rev 1",
+ "GeoIP City Edition V6, Rev 0",
+ "GeoIP Netspeed Edition, Rev 1",
+ "GeoIP Netspeed Edition V6, Rev1",
+ "GeoIP Country Confidence Edition",
+ "GeoIP City Confidence Edition",
+ "GeoIP Region Confidence Edition",
+ "GeoIP Postal Confidence Edition",
+ "GeoIP Accuracy Radius Edition V6"};
+
+char *GeoIP_custom_directory = NULL;
+
+void GeoIP_setup_custom_directory(char *dir) { GeoIP_custom_directory = dir; }
+
+char *_GeoIP_full_path_to(const char *file_name) {
+ int len;
+ char *path = malloc(sizeof(char) * 1024);
+
+ if (GeoIP_custom_directory == NULL) {
+#if !defined(_WIN32)
+ memset(path, 0, sizeof(char) * 1024);
+ snprintf(
+ path, sizeof(char) * 1024 - 1, "%s/%s", GEOIPDATADIR, file_name);
+#else
+ char buf[MAX_PATH], *p, *q = NULL;
+ memset(buf, 0, sizeof(buf));
+ len = GetModuleFileNameA(GetModuleHandle(NULL), buf, sizeof(buf) - 1);
+ for (p = buf + len; p > buf; p--) {
+ if (*p == '\\') {
+ if (!q) {
+ q = p;
+ } else {
+ *p = '/';
+ }
+ }
+ }
+ *q = 0;
+ memset(path, 0, sizeof(char) * 1024);
+ snprintf(path, sizeof(char) * 1024 - 1, "%s/%s", buf, file_name);
+#endif
+ } else {
+ len = strlen(GeoIP_custom_directory);
+ if (GeoIP_custom_directory[len - 1] != '/') {
+ snprintf(path,
+ sizeof(char) * 1024 - 1,
+ "%s/%s",
+ GeoIP_custom_directory,
+ file_name);
+ } else {
+ snprintf(path,
+ sizeof(char) * 1024 - 1,
+ "%s%s",
+ GeoIP_custom_directory,
+ file_name);
+ }
+ }
+ return path;
+}
+
+char **GeoIPDBFileName = NULL;
+
+void _GeoIP_setup_dbfilename(void) {
+ if (NULL == GeoIPDBFileName) {
+ GeoIPDBFileName = malloc(sizeof(char *) * NUM_DB_TYPES);
+ memset(GeoIPDBFileName, 0, sizeof(char *) * NUM_DB_TYPES);
+
+ GeoIPDBFileName[GEOIP_COUNTRY_EDITION] =
+ _GeoIP_full_path_to("GeoIP.dat");
+ GeoIPDBFileName[GEOIP_REGION_EDITION_REV0] =
+ _GeoIP_full_path_to("GeoIPRegion.dat");
+ GeoIPDBFileName[GEOIP_REGION_EDITION_REV1] =
+ _GeoIP_full_path_to("GeoIPRegion.dat");
+ GeoIPDBFileName[GEOIP_CITY_EDITION_REV0] =
+ _GeoIP_full_path_to("GeoIPCity.dat");
+ GeoIPDBFileName[GEOIP_CITY_EDITION_REV1] =
+ _GeoIP_full_path_to("GeoIPCity.dat");
+ GeoIPDBFileName[GEOIP_ISP_EDITION] =
+ _GeoIP_full_path_to("GeoIPISP.dat");
+ GeoIPDBFileName[GEOIP_ORG_EDITION] =
+ _GeoIP_full_path_to("GeoIPOrg.dat");
+ GeoIPDBFileName[GEOIP_PROXY_EDITION] =
+ _GeoIP_full_path_to("GeoIPProxy.dat");
+ GeoIPDBFileName[GEOIP_ASNUM_EDITION] =
+ _GeoIP_full_path_to("GeoIPASNum.dat");
+ GeoIPDBFileName[GEOIP_NETSPEED_EDITION] =
+ _GeoIP_full_path_to("GeoIPNetSpeed.dat");
+ GeoIPDBFileName[GEOIP_DOMAIN_EDITION] =
+ _GeoIP_full_path_to("GeoIPDomain.dat");
+ GeoIPDBFileName[GEOIP_COUNTRY_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPv6.dat");
+ GeoIPDBFileName[GEOIP_LOCATIONA_EDITION] =
+ _GeoIP_full_path_to("GeoIPLocA.dat");
+ GeoIPDBFileName[GEOIP_ACCURACYRADIUS_EDITION] =
+ _GeoIP_full_path_to("GeoIPDistance.dat");
+ GeoIPDBFileName[GEOIP_LARGE_COUNTRY_EDITION] =
+ _GeoIP_full_path_to("GeoIP.dat");
+ GeoIPDBFileName[GEOIP_LARGE_COUNTRY_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPv6.dat");
+ GeoIPDBFileName[GEOIP_ASNUM_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPASNumv6.dat");
+ GeoIPDBFileName[GEOIP_ISP_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPISPv6.dat");
+ GeoIPDBFileName[GEOIP_ORG_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPOrgv6.dat");
+ GeoIPDBFileName[GEOIP_DOMAIN_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPDomainv6.dat");
+ GeoIPDBFileName[GEOIP_LOCATIONA_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPLocAv6.dat");
+ GeoIPDBFileName[GEOIP_REGISTRAR_EDITION] =
+ _GeoIP_full_path_to("GeoIPRegistrar.dat");
+ GeoIPDBFileName[GEOIP_REGISTRAR_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPRegistrarv6.dat");
+ GeoIPDBFileName[GEOIP_USERTYPE_EDITION] =
+ _GeoIP_full_path_to("GeoIPUserType.dat");
+ GeoIPDBFileName[GEOIP_USERTYPE_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPUserTypev6.dat");
+ GeoIPDBFileName[GEOIP_CITY_EDITION_REV0_V6] =
+ _GeoIP_full_path_to("GeoIPCityv6.dat");
+ GeoIPDBFileName[GEOIP_CITY_EDITION_REV1_V6] =
+ _GeoIP_full_path_to("GeoIPCityv6.dat");
+ GeoIPDBFileName[GEOIP_NETSPEED_EDITION_REV1] =
+ _GeoIP_full_path_to("GeoIPNetSpeedCell.dat");
+ GeoIPDBFileName[GEOIP_NETSPEED_EDITION_REV1_V6] =
+ _GeoIP_full_path_to("GeoIPNetSpeedCellv6.dat");
+ GeoIPDBFileName[GEOIP_COUNTRYCONF_EDITION] =
+ _GeoIP_full_path_to("GeoIPCountryConf.dat");
+ GeoIPDBFileName[GEOIP_CITYCONF_EDITION] =
+ _GeoIP_full_path_to("GeoIPCityConf.dat");
+ GeoIPDBFileName[GEOIP_REGIONCONF_EDITION] =
+ _GeoIP_full_path_to("GeoIPRegionConf.dat");
+ GeoIPDBFileName[GEOIP_POSTALCONF_EDITION] =
+ _GeoIP_full_path_to("GeoIPPostalConf.dat");
+ GeoIPDBFileName[GEOIP_ACCURACYRADIUS_EDITION_V6] =
+ _GeoIP_full_path_to("GeoIPDistancev6.dat");
+ }
+}
+
+static int _file_exists(const char *file_name) {
+ struct stat file_stat;
+ return (stat(file_name, &file_stat) == 0) ? 1 : 0;
+}
+
+char *_GeoIP_iso_8859_1__utf8(const char *iso) {
+ signed char c;
+ char k;
+ char *p;
+ char *t = (char *)iso;
+ int len = 0;
+ while ((c = *t++)) {
+ if (c < 0) {
+ len++;
+ }
+ }
+ len += t - iso;
+ t = p = malloc(len);
+
+ if (p) {
+ while ((c = *iso++)) {
+ if (c < 0) {
+ k = (char)0xc2;
+ if (c >= -64) {
+ k++;
+ }
+ *t++ = k;
+ c &= ~0x40;
+ }
+ *t++ = c;
+ }
+ *t++ = 0x00;
+ }
+ return p;
+}
+
+int GeoIP_is_private_ipnum_v4(unsigned long ipnum) {
+ return ((ipnum >= 167772160U && ipnum <= 184549375U) ||
+ (ipnum >= 2851995648U && ipnum <= 2852061183U) ||
+ (ipnum >= 2886729728U && ipnum <= 2887778303U) ||
+ (ipnum >= 3232235520U && ipnum <= 3232301055U) ||
+ (ipnum >= 2130706432U && ipnum <= 2147483647U))
+ ? 1
+ : 0;
+}
+
+int GeoIP_is_private_v4(const char *addr) {
+ unsigned long ipnum = GeoIP_addr_to_num(addr);
+ return GeoIP_is_private_ipnum_v4(ipnum);
+}
+
+int GeoIP_db_avail(int type) {
+ const char *filePath;
+ if (type < 0 || type >= NUM_DB_TYPES) {
+ return 0;
+ }
+ _GeoIP_setup_dbfilename();
+ filePath = GeoIPDBFileName[type];
+ if (NULL == filePath) {
+ return 0;
+ }
+ return _file_exists(filePath);
+}
+
+static int _database_has_content(int database_type) {
+ return (database_type != GEOIP_COUNTRY_EDITION &&
+ database_type != GEOIP_PROXY_EDITION &&
+ database_type != GEOIP_NETSPEED_EDITION &&
+ database_type != GEOIP_COUNTRY_EDITION_V6 &&
+ database_type != GEOIP_LARGE_COUNTRY_EDITION &&
+ database_type != GEOIP_LARGE_COUNTRY_EDITION_V6 &&
+ database_type != GEOIP_REGION_EDITION_REV0 &&
+ database_type != GEOIP_REGION_EDITION_REV1)
+ ? 1
+ : 0;
+}
+
+static ssize_t get_index_size(GeoIP *gi, struct stat *buf) {
+ ssize_t index_size;
+ unsigned int segment;
+
+ if (!_database_has_content(gi->databaseType)) {
+ return buf->st_size;
+ }
+
+ segment = gi->databaseSegments[0];
+ index_size = segment * (ssize_t)gi->record_length * 2;
+
+ /* check for overflow in multiplication */
+ if (segment != 0 &&
+ index_size / segment != (ssize_t)gi->record_length * 2) {
+ return -1;
+ }
+
+ /* Index size should never exceed the size of the file */
+ if (index_size > buf->st_size) {
+ return -1;
+ }
+
+ return index_size;
+}
+
+static void _setup_segments(GeoIP *gi) {
+ int i, j, segment_record_length;
+ unsigned char delim[3];
+ unsigned char buf[LARGE_SEGMENT_RECORD_LENGTH];
+ off_t offset = gi->size - 3;
+ int fno = fileno(gi->GeoIPDatabase);
+
+ gi->databaseSegments = NULL;
+
+ /* default to GeoIP Country Edition */
+ gi->databaseType = GEOIP_COUNTRY_EDITION;
+ gi->record_length = STANDARD_RECORD_LENGTH;
+
+ for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
+ if (pread(fno, delim, 3, offset) != 3) {
+ return;
+ }
+ offset += 3;
+ if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255) {
+ if (pread(fno, &gi->databaseType, 1, offset) != 1) {
+ return;
+ }
+ offset++;
+ if (gi->databaseType >= 106) {
+ /* backwards compatibility with databases from April 2003 and
+ * earlier */
+ gi->databaseType -= 105;
+ }
+
+ if (gi->databaseType == GEOIP_REGION_EDITION_REV0) {
+ /* Region Edition, pre June 2003 */
+ gi->databaseSegments = malloc(sizeof(unsigned int));
+ if (gi->databaseSegments == NULL) {
+ return;
+ }
+ gi->databaseSegments[0] = STATE_BEGIN_REV0;
+ } else if (gi->databaseType == GEOIP_REGION_EDITION_REV1) {
+ /* Region Edition, post June 2003 */
+ gi->databaseSegments = malloc(sizeof(unsigned int));
+ if (gi->databaseSegments == NULL) {
+ return;
+ }
+ gi->databaseSegments[0] = STATE_BEGIN_REV1;
+ } else if (gi->databaseType == GEOIP_CITY_EDITION_REV0 ||
+ gi->databaseType == GEOIP_CITY_EDITION_REV1 ||
+ gi->databaseType == GEOIP_ORG_EDITION ||
+ gi->databaseType == GEOIP_ORG_EDITION_V6 ||
+ gi->databaseType == GEOIP_DOMAIN_EDITION ||
+ gi->databaseType == GEOIP_DOMAIN_EDITION_V6 ||
+ gi->databaseType == GEOIP_ISP_EDITION ||
+ gi->databaseType == GEOIP_ISP_EDITION_V6 ||
+ gi->databaseType == GEOIP_REGISTRAR_EDITION ||
+ gi->databaseType == GEOIP_REGISTRAR_EDITION_V6 ||
+ gi->databaseType == GEOIP_USERTYPE_EDITION ||
+ gi->databaseType == GEOIP_USERTYPE_EDITION_V6 ||
+ gi->databaseType == GEOIP_ASNUM_EDITION ||
+ gi->databaseType == GEOIP_ASNUM_EDITION_V6 ||
+ gi->databaseType == GEOIP_NETSPEED_EDITION_REV1 ||
+ gi->databaseType == GEOIP_NETSPEED_EDITION_REV1_V6 ||
+ gi->databaseType == GEOIP_LOCATIONA_EDITION ||
+ gi->databaseType == GEOIP_ACCURACYRADIUS_EDITION ||
+ gi->databaseType == GEOIP_ACCURACYRADIUS_EDITION_V6 ||
+ gi->databaseType == GEOIP_CITY_EDITION_REV0_V6 ||
+ gi->databaseType == GEOIP_CITY_EDITION_REV1_V6 ||
+ gi->databaseType == GEOIP_CITYCONF_EDITION ||
+ gi->databaseType == GEOIP_COUNTRYCONF_EDITION ||
+ gi->databaseType == GEOIP_REGIONCONF_EDITION ||
+ gi->databaseType == GEOIP_POSTALCONF_EDITION) {
+ /* City/Org Editions have two segments, read offset of second
+ * segment */
+ gi->databaseSegments = malloc(sizeof(unsigned int));
+ if (gi->databaseSegments == NULL) {
+ return;
+ }
+ gi->databaseSegments[0] = 0;
+ segment_record_length = SEGMENT_RECORD_LENGTH;
+
+ if (pread(fno, buf, segment_record_length, offset) !=
+ segment_record_length) {
+ free(gi->databaseSegments);
+ gi->databaseSegments = NULL;
+ return;
+ }
+
+ for (j = 0; j < segment_record_length; j++) {
+ gi->databaseSegments[0] += (buf[j] << (j * 8));
+ }
+
+ /* the record_length must be correct from here on */
+ if (gi->databaseType == GEOIP_ORG_EDITION ||
+ gi->databaseType == GEOIP_ORG_EDITION_V6 ||
+ gi->databaseType == GEOIP_DOMAIN_EDITION ||
+ gi->databaseType == GEOIP_DOMAIN_EDITION_V6 ||
+ gi->databaseType == GEOIP_ISP_EDITION ||
+ gi->databaseType == GEOIP_ISP_EDITION_V6) {
+ gi->record_length = ORG_RECORD_LENGTH;
+ }
+ }
+ break;
+ } else {
+ offset -= 4;
+ if (offset < 0) {
+ gi->databaseSegments = NULL;
+ return;
+ }
+ }
+ }
+ if (gi->databaseType == GEOIP_COUNTRY_EDITION ||
+ gi->databaseType == GEOIP_PROXY_EDITION ||
+ gi->databaseType == GEOIP_NETSPEED_EDITION ||
+ gi->databaseType == GEOIP_COUNTRY_EDITION_V6) {
+ gi->databaseSegments = malloc(sizeof(unsigned int));
+ if (gi->databaseSegments == NULL) {
+ return;
+ }
+ gi->databaseSegments[0] = COUNTRY_BEGIN;
+ } else if (gi->databaseType == GEOIP_LARGE_COUNTRY_EDITION ||
+ gi->databaseType == GEOIP_LARGE_COUNTRY_EDITION_V6) {
+ gi->databaseSegments = malloc(sizeof(unsigned int));
+ if (gi->databaseSegments == NULL) {
+ return;
+ }
+ gi->databaseSegments[0] = LARGE_COUNTRY_BEGIN;
+ }
+}
+
+static void _check_mtime(GeoIP *gi) {
+ struct stat buf;
+ ssize_t idx_size;
+
+#if !defined(_WIN32)
+ struct timeval t;
+#else /* !defined(_WIN32) */
+ FILETIME ft;
+ ULONGLONG t;
+#endif /* !defined(_WIN32) */
+
+ if (gi->flags & GEOIP_CHECK_CACHE) {
+
+#if !defined(_WIN32)
+ /* stat only has second granularity, so don't
+ * call it more than once a second */
+ if (0 != gettimeofday(&t, NULL)) {
+ DEBUG_MSGF(
+ gi->flags, "Error calling gettimeofday: %s\n", strerror(errno));
+ return;
+ }
+
+ if (t.tv_sec == gi->last_mtime_check) {
+ return;
+ }
+ gi->last_mtime_check = t.tv_sec;
+
+#else /* !defined(_WIN32) */
+
+ /* stat only has second granularity, so don't
+ call it more than once a second */
+ GetSystemTimeAsFileTime(&ft);
+ t = FILETIME_TO_USEC(ft) / 1000 / 1000;
+ if (t == gi->last_mtime_check) {
+ return;
+ }
+ gi->last_mtime_check = t;
+
+#endif /* !defined(_WIN32) */
+
+ if (stat(gi->file_path, &buf) != -1) {
+ /* make sure that the database file is at least 60
+ * seconds untouched. Otherwise we might load the
+ * database only partly and crash
+ */
+ if (buf.st_mtime != gi->mtime &&
+ (buf.st_mtime + 60 < gi->last_mtime_check)) {
+ /* GeoIP Database file updated */
+ if (gi->flags & (GEOIP_MEMORY_CACHE | GEOIP_MMAP_CACHE)) {
+ if (gi->cache && (gi->flags & GEOIP_MMAP_CACHE)) {
+#if !defined(_WIN32)
+ /* MMAP is only avail on UNIX */
+ munmap(gi->cache, gi->size);
+ gi->cache = NULL;
+#endif
+ } else {
+ /* reload database into memory cache */
+ if ((gi->cache = (unsigned char *)realloc(
+ gi->cache, buf.st_size)) == NULL) {
+ DEBUG_MSGF(gi->flags,
+ "Out of memory when reloading %s\n",
+ gi->file_path);
+ return;
+ }
+ }
+ }
+ /* refresh filehandle */
+ fclose(gi->GeoIPDatabase);
+ gi->GeoIPDatabase = fopen(gi->file_path, "rb");
+ if (gi->GeoIPDatabase == NULL) {
+ DEBUG_MSGF(gi->flags,
+ "Error Opening file %s when reloading\n",
+ gi->file_path);
+ return;
+ }
+ gi->mtime = buf.st_mtime;
+ gi->size = buf.st_size;
+
+ if (gi->flags & GEOIP_MMAP_CACHE) {
+#if defined(_WIN32)
+ DEBUG_MSGF(gi->flags,
+ "GEOIP_MMAP_CACHE is not supported on WIN32\n");
+ gi->cache = 0;
+ return;
+#else
+ gi->cache = mmap(NULL,
+ buf.st_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ fileno(gi->GeoIPDatabase),
+ 0);
+ if (gi->cache == MAP_FAILED) {
+
+ DEBUG_MSGF(gi->flags,
+ "Error remapping file %s when reloading\n",
+ gi->file_path);
+
+ gi->cache = NULL;
+ return;
+ }
+#endif
+ } else if (gi->flags & GEOIP_MEMORY_CACHE) {
+ if (pread(fileno(gi->GeoIPDatabase),
+ gi->cache,
+ buf.st_size,
+ 0) != (ssize_t)buf.st_size) {
+ DEBUG_MSGF(gi->flags,
+ "Error reading file %s when reloading\n",
+ gi->file_path);
+ return;
+ }
+ }
+
+ if (gi->databaseSegments != NULL) {
+ free(gi->databaseSegments);
+ gi->databaseSegments = NULL;
+ }
+ _setup_segments(gi);
+ if (gi->databaseSegments == NULL) {
+ DEBUG_MSGF(gi->flags,
+ "Error reading file %s -- corrupt\n",
+ gi->file_path);
+ return;
+ }
+
+ idx_size = get_index_size(gi, &buf);
+ if (idx_size < 0) {
+ DEBUG_MSGF(
+ gi->flags, "Error file %s -- corrupt\n", gi->file_path);
+ return;
+ }
+
+ if (gi->flags & GEOIP_INDEX_CACHE) {
+ gi->index_cache = (unsigned char *)realloc(
+ gi->index_cache, sizeof(unsigned char) * idx_size);
+ if (gi->index_cache != NULL) {
+ if (pread(fileno(gi->GeoIPDatabase),
+ gi->index_cache,
+ idx_size,
+ 0) != idx_size) {
+ DEBUG_MSGF(
+ gi->flags,
+ "Error reading file %s where reloading\n",
+ gi->file_path);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+#define ADDR_STR_LEN (8 * 4 + 7 + 1)
+unsigned int
+_GeoIP_seek_record_v6_gl(GeoIP *gi, geoipv6_t ipnum, GeoIPLookup *gl) {
+ int depth;
+ char paddr[ADDR_STR_LEN];
+ unsigned int x;
+ unsigned char stack_buffer[2 * MAX_RECORD_LENGTH];
+ const unsigned char *buf = (gi->cache == NULL) ? stack_buffer : NULL;
+ unsigned int offset = 0;
+
+ const unsigned char *p;
+ int j;
+ int fno = fileno(gi->GeoIPDatabase);
+
+ unsigned int record_pair_length = gi->record_length * 2;
+
+ _check_mtime(gi);
+ if (GeoIP_teredo(gi)) {
+ __GEOIP_PREPARE_TEREDO(&ipnum);
+ }
+ for (depth = 127; depth >= 0; depth--) {
+ unsigned int byte_offset = record_pair_length * offset;
+ if (byte_offset > gi->size - record_pair_length) {
+ /* The pointer is invalid */
+ break;
+ }
+ if (gi->cache == NULL && gi->index_cache == NULL) {
+ /* read from disk */
+ if (pread(
+ fno, stack_buffer, record_pair_length, (long)byte_offset) !=
+ record_pair_length) {
+ break;
+ }
+ } else if (gi->index_cache == NULL) {
+ /* simply point to record in memory */
+ buf = gi->cache + (long)byte_offset;
+ } else {
+ buf = gi->index_cache + (long)byte_offset;
+ }
+
+ if (GEOIP_CHKBIT_V6(depth, ipnum.s6_addr)) {
+ /* Take the right-hand branch */
+ if (gi->record_length == 3) {
+ /* Most common case is completely unrolled and uses constants.
+ */
+ x = (buf[3 * 1 + 0] << (0 * 8)) + (buf[3 * 1 + 1] << (1 * 8)) +
+ (buf[3 * 1 + 2] << (2 * 8));
+
+ } else {
+ /* General case */
+ j = gi->record_length;
+ p = &buf[2 * j];
+ x = 0;
+ do {
+ x <<= 8;
+ x += *(--p);
+ } while (--j);
+ }
+
+ } else {
+ /* Take the left-hand branch */
+ if (gi->record_length == 3) {
+ /* Most common case is completely unrolled and uses constants.
+ */
+ x = (buf[3 * 0 + 0] << (0 * 8)) + (buf[3 * 0 + 1] << (1 * 8)) +
+ (buf[3 * 0 + 2] << (2 * 8));
+ } else {
+ /* General case */
+ j = gi->record_length;
+ p = &buf[1 * j];
+ x = 0;
+ do {
+ x <<= 8;
+ x += *(--p);
+ } while (--j);
+ }
+ }
+
+ if (x >= gi->databaseSegments[0]) {
+ gi->netmask = gl->netmask = 128 - depth;
+ return x;
+ }
+ offset = x;
+ }
+
+ /* shouldn't reach here */
+ _GeoIP_inet_ntop(AF_INET6, &ipnum.s6_addr[0], paddr, ADDR_STR_LEN);
+ DEBUG_MSGF(gi->flags,
+ "Error Traversing Database for ipnum = %s - Perhaps database is "
+ "corrupt?\n",
+ paddr);
+ return 0;
+}
+
+geoipv6_t _GeoIP_addr_to_num_v6(const char *addr) {
+ geoipv6_t ipnum;
+ if (1 == _GeoIP_inet_pton(AF_INET6, addr, &ipnum.s6_addr[0])) {
+ return ipnum;
+ }
+ return IPV6_NULL;
+}
+
+unsigned int
+_GeoIP_seek_record_gl(GeoIP *gi, unsigned long ipnum, GeoIPLookup *gl) {
+ int depth;
+ unsigned int x;
+ unsigned char stack_buffer[2 * MAX_RECORD_LENGTH];
+ const unsigned char *buf = (gi->cache == NULL) ? stack_buffer : NULL;
+ unsigned int offset = 0;
+
+ const unsigned char *p;
+ int j;
+ int fno = fileno(gi->GeoIPDatabase);
+
+ unsigned int record_pair_length = gi->record_length * 2;
+
+ _check_mtime(gi);
+ for (depth = 31; depth >= 0; depth--) {
+ unsigned int byte_offset = record_pair_length * offset;
+ if (byte_offset > gi->size - record_pair_length) {
+ /* The pointer is invalid */
+ break;
+ }
+ if (gi->cache == NULL && gi->index_cache == NULL) {
+ /* read from disk */
+ if (pread(fno, stack_buffer, record_pair_length, byte_offset) !=
+ record_pair_length) {
+ break;
+ }
+ } else if (gi->index_cache == NULL) {
+ /* simply point to record in memory */
+ buf = gi->cache + (long)byte_offset;
+ } else {
+ buf = gi->index_cache + (long)byte_offset;
+ }
+
+ if (ipnum & (1 << depth)) {
+ /* Take the right-hand branch */
+ if (gi->record_length == 3) {
+ /* Most common case is completely unrolled and uses constants.
+ */
+ x = (buf[3 * 1 + 0] << (0 * 8)) + (buf[3 * 1 + 1] << (1 * 8)) +
+ (buf[3 * 1 + 2] << (2 * 8));
+
+ } else {
+ /* General case */
+ j = gi->record_length;
+ p = &buf[2 * j];
+ x = 0;
+ do {
+ x <<= 8;
+ x += *(--p);
+ } while (--j);
+ }
+
+ } else {
+ /* Take the left-hand branch */
+ if (gi->record_length == 3) {
+ /* Most common case is completely unrolled and uses constants.
+ */
+ x = (buf[3 * 0 + 0] << (0 * 8)) + (buf[3 * 0 + 1] << (1 * 8)) +
+ (buf[3 * 0 + 2] << (2 * 8));
+ } else {
+ /* General case */
+ j = gi->record_length;
+ p = &buf[1 * j];
+ x = 0;
+ do {
+ x <<= 8;
+ x += *(--p);
+ } while (--j);
+ }
+ }
+
+ if (x >= gi->databaseSegments[0]) {
+ gi->netmask = gl->netmask = 32 - depth;
+ return x;
+ }
+ offset = x;
+ }
+ /* shouldn't reach here */
+ DEBUG_MSGF(gi->flags,
+ "Error Traversing Database for ipnum = %lu - Perhaps database "
+ "is corrupt?\n",
+ ipnum);
+ return 0;
+}
+
+unsigned long GeoIP_addr_to_num(const char *addr) {
+ unsigned int c, octet, t;
+ unsigned long ipnum;
+ int i = 3;
+
+ octet = ipnum = 0;
+ while ((c = *addr++)) {
+ if (c == '.') {
+ if (octet > 255) {
+ return 0;
+ }
+ ipnum <<= 8;
+ ipnum += octet;
+ i--;
+ octet = 0;
+ } else {
+ t = octet;
+ octet <<= 3;
+ octet += t;
+ octet += t;
+ c -= '0';
+ if (c > 9) {
+ return 0;
+ }
+ octet += c;
+ }
+ }
+ if ((octet > 255) || (i != 0)) {
+ return 0;
+ }
+ ipnum <<= 8;
+ return ipnum + octet;
+}
+
+GeoIP *GeoIP_open_type(int type, int flags) {
+ GeoIP *gi;
+ const char *filePath;
+ if (type < 0 || type >= NUM_DB_TYPES) {
+ printf("Invalid database type %d\n", type);
+ return NULL;
+ }
+ _GeoIP_setup_dbfilename();
+ filePath = GeoIPDBFileName[type];
+ if (filePath == NULL) {
+ printf("Invalid database type %d\n", type);
+ return NULL;
+ }
+ gi = GeoIP_open(filePath, flags);
+
+ if (gi) {
+ /* make sure this is the requested database type */
+ int database_type = gi->databaseType;
+ if (database_type > 105) {
+ database_type -= 105;
+ }
+ /* type must match, but we accept org and asnum,
+ * since domain and *conf database have always the wrong type
+ * for historical reason. Maybe we fix it at some point.
+ */
+ if (database_type == type || database_type == GEOIP_ASNUM_EDITION ||
+ database_type == GEOIP_ORG_EDITION) {
+ return gi;
+ }
+ GeoIP_delete(gi);
+ }
+
+ return NULL;
+}
+
+GeoIP *GeoIP_new(int flags) {
+ GeoIP *gi;
+ _GeoIP_setup_dbfilename();
+ gi = GeoIP_open(GeoIPDBFileName[GEOIP_COUNTRY_EDITION], flags);
+ return gi;
+}
+
+GeoIP *GeoIP_open(const char *filename, int flags) {
+ struct stat buf;
+ ssize_t idx_size;
+ GeoIP *gi;
+ size_t len;
+
+ gi = (GeoIP *)calloc(1, sizeof(GeoIP));
+ if (gi == NULL) {
+ return NULL;
+ }
+ len = sizeof(char) * (strlen(filename) + 1);
+ gi->file_path = malloc(len);
+ if (gi->file_path == NULL) {
+ free(gi);
+ return NULL;
+ }
+ strncpy(gi->file_path, filename, len);
+ gi->GeoIPDatabase = fopen(filename, "rb");
+ if (gi->GeoIPDatabase == NULL) {
+ DEBUG_MSGF(flags, "Error Opening file %s\n", filename);
+ GeoIP_delete(gi);
+ return NULL;
+ }
+
+ if (fstat(fileno(gi->GeoIPDatabase), &buf) == -1) {
+ DEBUG_MSGF(flags, "Error stating file %s\n", filename);
+ GeoIP_delete(gi);
+ return NULL;
+ }
+
+ gi->size = buf.st_size;
+ if (flags & (GEOIP_MEMORY_CACHE | GEOIP_MMAP_CACHE)) {
+ gi->mtime = buf.st_mtime;
+
+ /* MMAP added my Peter Shipley */
+ if (flags & GEOIP_MMAP_CACHE) {
+#if !defined(_WIN32)
+ gi->cache = mmap(NULL,
+ buf.st_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ fileno(gi->GeoIPDatabase),
+ 0);
+ if (gi->cache == MAP_FAILED) {
+ DEBUG_MSGF(flags, "Error mmaping file %s\n", filename);
+ GeoIP_delete(gi);
+ return NULL;
+ }
+#endif
+ } else {
+ gi->cache =
+ (unsigned char *)malloc(sizeof(unsigned char) * buf.st_size);
+
+ if (gi->cache != NULL) {
+ if (pread(
+ fileno(gi->GeoIPDatabase), gi->cache, buf.st_size, 0) !=
+ (ssize_t)buf.st_size) {
+ DEBUG_MSGF(flags, "Error reading file %s\n", filename);
+ GeoIP_delete(gi);
+ return NULL;
+ }
+ }
+ }
+ } else {
+ if (flags & GEOIP_CHECK_CACHE) {
+ if (fstat(fileno(gi->GeoIPDatabase), &buf) == -1) {
+ DEBUG_MSGF(flags, "Error stating file %s\n", filename);
+ GeoIP_delete(gi);
+ return NULL;
+ }
+ gi->mtime = buf.st_mtime;
+ }
+ gi->cache = NULL;
+ }
+ gi->flags = flags;
+ gi->charset = GEOIP_CHARSET_ISO_8859_1;
+ gi->ext_flags = 1U << GEOIP_TEREDO_BIT;
+ _setup_segments(gi);
+ if (gi->databaseSegments == NULL) {
+ DEBUG_MSGF(
+ gi->flags, "Error reading file %s -- corrupt\n", gi->file_path);
+ GeoIP_delete(gi);
+ return NULL;
+ }
+
+ idx_size = get_index_size(gi, &buf);
+
+ if (idx_size < 0) {
+ DEBUG_MSGF(gi->flags, "Error file %s -- corrupt\n", gi->file_path);
+ GeoIP_delete(gi);
+ return NULL;
+ }
+
+ if (flags & GEOIP_INDEX_CACHE) {
+ gi->index_cache =
+ (unsigned char *)malloc(sizeof(unsigned char) * idx_size);
+ if (gi->index_cache != NULL) {
+ if (pread(
+ fileno(gi->GeoIPDatabase), gi->index_cache, idx_size, 0) !=
+ idx_size) {
+ DEBUG_MSGF(gi->flags, "Error reading file %s\n", filename);
+ GeoIP_delete(gi);
+ return NULL;
+ }
+ }
+ } else {
+ gi->index_cache = NULL;
+ }
+
+ gi->last_mtime_check = 0;
+
+ return gi;
+}
+
+void GeoIP_delete(GeoIP *gi) {
+ if (gi == NULL) {
+ return;
+ }
+ if (gi->GeoIPDatabase != NULL) {
+ fclose(gi->GeoIPDatabase);
+ }
+ if (gi->cache != NULL) {
+ if (gi->flags & GEOIP_MMAP_CACHE) {
+#if !defined(_WIN32)
+ if (gi->cache) {
+ munmap(gi->cache, gi->size);
+ }
+#endif
+ } else {
+ free(gi->cache);
+ }
+ gi->cache = NULL;
+ }
+ free(gi->index_cache);
+ free(gi->file_path);
+ free(gi->databaseSegments);
+ free(gi);
+}
+
+const char *
+GeoIP_country_code_by_name_v6_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_name_v6_gl(gi, name, gl);
+ return (country_id > 0) ? GeoIP_code_by_id(country_id) : NULL;
+}
+
+const char *
+GeoIP_country_code_by_name_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_name_gl(gi, name, gl);
+ return (country_id > 0) ? GeoIP_code_by_id(country_id) : NULL;
+}
+
+const char *GeoIP_country_code3_by_name_v6_gl(GeoIP *gi,
+ const char *name,
+ GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_name_v6_gl(gi, name, gl);
+ return (country_id > 0) ? GeoIP_code3_by_id(country_id) : NULL;
+}
+
+const char *
+GeoIP_country_code3_by_name_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_name_gl(gi, name, gl);
+ return (country_id > 0) ? GeoIP_code3_by_id(country_id) : NULL;
+}
+
+const char *
+GeoIP_country_name_by_name_v6_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_name_v6_gl(gi, name, gl);
+ return GeoIP_country_name_by_id(gi, country_id);
+}
+
+const char *
+GeoIP_country_name_by_name_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_name_gl(gi, name, gl);
+ return GeoIP_country_name_by_id(gi, country_id);
+}
+
+unsigned long _GeoIP_lookupaddress(const char *host) {
+ unsigned long addr = inet_addr(host);
+ struct hostent phe2;
+ struct hostent *phe = &phe2;
+ char *buf = NULL;
+#ifdef HAVE_GETHOSTBYNAME_R
+ int buflength = 16384;
+ int herr = 0;
+#endif
+ int result = 0;
+#ifdef HAVE_GETHOSTBYNAME_R
+ buf = malloc(buflength);
+#endif
+ if (addr == INADDR_NONE) {
+#ifdef HAVE_GETHOSTBYNAME_R
+ while (1) {
+/* we use gethostbyname_r here because it is thread-safe and gethostbyname is
+ * not */
+#ifdef GETHOSTBYNAME_R_RETURNS_INT
+ result = gethostbyname_r(host, &phe2, buf, buflength, &phe, &herr);
+#else
+ phe = gethostbyname_r(host, &phe2, buf, buflength, &herr);
+#endif
+ if (herr != ERANGE) {
+ break;
+ }
+ if (result == 0) {
+ break;
+ }
+ /* double the buffer if the buffer is too small */
+ buflength = buflength * 2;
+ buf = realloc(buf, buflength);
+ }
+#else
+ /* Some systems do not support gethostbyname_r, such as Mac OS X */
+ phe = gethostbyname(host);
+#endif
+ if (!phe || result != 0) {
+ free(buf);
+ return 0;
+ }
+#if !defined(_WIN32)
+ addr = *((in_addr_t *)phe->h_addr_list[0]);
+#else
+ addr = ((IN_ADDR *)phe->h_addr_list[0])->S_un.S_addr;
+#endif
+ }
+#ifdef HAVE_GETHOSTBYNAME_R
+ free(buf);
+#endif
+ return ntohl(addr);
+}
+
+geoipv6_t _GeoIP_lookupaddress_v6(const char *host) {
+ geoipv6_t ipnum;
+ int gaierr;
+ struct addrinfo hints, *aifirst;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ /* hints.ai_flags = AI_V4MAPPED; */
+ hints.ai_socktype = SOCK_STREAM;
+
+ if ((gaierr = getaddrinfo(host, NULL, &hints, &aifirst)) != 0) {
+ /* DEBUG_MSGF("Err: %s (%d %s)\n", host, gaierr, gai_strerror(gaierr));
+ */
+ return IPV6_NULL;
+ }
+ memcpy(ipnum.s6_addr,
+ ((struct sockaddr_in6 *)aifirst->ai_addr)->sin6_addr.s6_addr,
+ sizeof(geoipv6_t));
+ freeaddrinfo(aifirst);
+ /* inet_pton(AF_INET6, host, ipnum.s6_addr); */
+
+ return ipnum;
+}
+
+int GeoIP_id_by_name_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ unsigned long ipnum;
+ int ret;
+ if (name == NULL) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_LARGE_COUNTRY_EDITION &&
+ gi->databaseType != GEOIP_COUNTRY_EDITION &&
+ gi->databaseType != GEOIP_PROXY_EDITION &&
+ gi->databaseType != GEOIP_NETSPEED_EDITION) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_COUNTRY_EDITION));
+ return 0;
+ }
+ if (!(ipnum = _GeoIP_lookupaddress(name))) {
+ return 0;
+ }
+ ret = _GeoIP_seek_record_gl(gi, ipnum, gl) - gi->databaseSegments[0];
+ return ret;
+}
+
+int GeoIP_id_by_name_v6_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ geoipv6_t ipnum;
+ int ret;
+ if (name == NULL) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_LARGE_COUNTRY_EDITION_V6 &&
+ gi->databaseType != GEOIP_COUNTRY_EDITION_V6) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_COUNTRY_EDITION_V6));
+ return 0;
+ }
+ ipnum = _GeoIP_lookupaddress_v6(name);
+ if (__GEOIP_V6_IS_NULL(ipnum)) {
+ return 0;
+ }
+
+ ret = _GeoIP_seek_record_v6_gl(gi, ipnum, gl) - gi->databaseSegments[0];
+ return ret;
+}
+
+const char *
+GeoIP_country_code_by_addr_v6_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_addr_v6_gl(gi, addr, gl);
+ return (country_id > 0) ? GeoIP_code_by_id(country_id) : NULL;
+}
+
+const char *
+GeoIP_country_code_by_addr_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_addr_gl(gi, addr, gl);
+ return (country_id > 0) ? GeoIP_code_by_id(country_id) : NULL;
+}
+const char *GeoIP_country_code3_by_addr_v6_gl(GeoIP *gi,
+ const char *addr,
+ GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_addr_v6_gl(gi, addr, gl);
+ return (country_id > 0) ? GeoIP_code3_by_id(country_id) : NULL;
+}
+
+const char *
+GeoIP_country_code3_by_addr_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_addr_gl(gi, addr, gl);
+ return (country_id > 0) ? GeoIP_code3_by_id(country_id) : NULL;
+}
+
+const char *
+GeoIP_country_name_by_addr_v6_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_addr_v6_gl(gi, addr, gl);
+ return GeoIP_country_name_by_id(gi, country_id);
+}
+
+const char *
+GeoIP_country_name_by_addr_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_addr_gl(gi, addr, gl);
+ return GeoIP_country_name_by_id(gi, country_id);
+}
+
+const char *GeoIP_country_name_by_ipnum_gl(GeoIP *gi,
+ unsigned long ipnum,
+ GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_ipnum_gl(gi, ipnum, gl);
+ return GeoIP_country_name_by_id(gi, country_id);
+}
+
+const char *
+GeoIP_country_name_by_ipnum_v6_gl(GeoIP *gi, geoipv6_t ipnum, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_ipnum_v6_gl(gi, ipnum, gl);
+ return GeoIP_country_name_by_id(gi, country_id);
+}
+
+const char *GeoIP_country_code_by_ipnum_gl(GeoIP *gi,
+ unsigned long ipnum,
+ GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_ipnum_gl(gi, ipnum, gl);
+ return (country_id > 0) ? GeoIP_code_by_id(country_id) : NULL;
+}
+
+const char *
+GeoIP_country_code_by_ipnum_v6_gl(GeoIP *gi, geoipv6_t ipnum, GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_ipnum_v6_gl(gi, ipnum, gl);
+ return (country_id > 0) ? GeoIP_code_by_id(country_id) : NULL;
+}
+
+const char *GeoIP_country_code3_by_ipnum_gl(GeoIP *gi,
+ unsigned long ipnum,
+ GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_ipnum_gl(gi, ipnum, gl);
+ return (country_id > 0) ? GeoIP_code3_by_id(country_id) : NULL;
+}
+
+const char *GeoIP_country_code3_by_ipnum_v6_gl(GeoIP *gi,
+ geoipv6_t ipnum,
+ GeoIPLookup *gl) {
+ int country_id;
+ country_id = GeoIP_id_by_ipnum_v6_gl(gi, ipnum, gl);
+ return (country_id > 0) ? GeoIP_code3_by_id(country_id) : NULL;
+}
+
+int GeoIP_country_id_by_addr_v6_gl(GeoIP *gi,
+ const char *addr,
+ GeoIPLookup *gl) {
+ return GeoIP_id_by_addr_v6_gl(gi, addr, gl);
+}
+
+int GeoIP_country_id_by_addr_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ return GeoIP_id_by_addr_gl(gi, addr, gl);
+}
+
+int GeoIP_country_id_by_name_v6_gl(GeoIP *gi,
+ const char *host,
+ GeoIPLookup *gl) {
+ return GeoIP_id_by_name_v6_gl(gi, host, gl);
+}
+
+int GeoIP_country_id_by_name_gl(GeoIP *gi, const char *host, GeoIPLookup *gl) {
+ return GeoIP_id_by_name_gl(gi, host, gl);
+}
+
+int GeoIP_id_by_addr_v6_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ geoipv6_t ipnum;
+ int ret;
+ if (addr == NULL) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_COUNTRY_EDITION_V6 &&
+ gi->databaseType != GEOIP_LARGE_COUNTRY_EDITION_V6) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_COUNTRY_EDITION_V6));
+ return 0;
+ }
+ ipnum = _GeoIP_addr_to_num_v6(addr);
+ ret = _GeoIP_seek_record_v6_gl(gi, ipnum, gl) - gi->databaseSegments[0];
+ return ret;
+}
+
+int GeoIP_id_by_addr_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ unsigned long ipnum;
+ int ret;
+ if (addr == NULL) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_COUNTRY_EDITION &&
+ gi->databaseType != GEOIP_LARGE_COUNTRY_EDITION &&
+ gi->databaseType != GEOIP_PROXY_EDITION &&
+ gi->databaseType != GEOIP_NETSPEED_EDITION) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_COUNTRY_EDITION));
+ return 0;
+ }
+ ipnum = GeoIP_addr_to_num(addr);
+ ret = _GeoIP_seek_record_gl(gi, ipnum, gl) - gi->databaseSegments[0];
+ return ret;
+}
+
+int GeoIP_id_by_ipnum_v6_gl(GeoIP *gi, geoipv6_t ipnum, GeoIPLookup *gl) {
+ int ret;
+ if (gi->databaseType != GEOIP_COUNTRY_EDITION_V6 &&
+ gi->databaseType != GEOIP_LARGE_COUNTRY_EDITION_V6) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_COUNTRY_EDITION_V6));
+ return 0;
+ }
+ ret = _GeoIP_seek_record_v6_gl(gi, ipnum, gl) - gi->databaseSegments[0];
+ return ret;
+}
+
+int GeoIP_id_by_ipnum_gl(GeoIP *gi, unsigned long ipnum, GeoIPLookup *gl) {
+ int ret;
+ if (ipnum == 0) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_COUNTRY_EDITION &&
+ gi->databaseType != GEOIP_LARGE_COUNTRY_EDITION &&
+ gi->databaseType != GEOIP_PROXY_EDITION &&
+ gi->databaseType != GEOIP_NETSPEED_EDITION) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_COUNTRY_EDITION));
+ return 0;
+ }
+ ret = _GeoIP_seek_record_gl(gi, ipnum, gl) - gi->databaseSegments[0];
+ return ret;
+}
+
+char *GeoIP_database_info(GeoIP *gi) {
+ int i;
+ unsigned char buf[3];
+ char *retval;
+ int has_structure_info = 0;
+ off_t offset = gi->size - 3;
+ int fno;
+
+ if (gi == NULL) {
+ return NULL;
+ }
+
+ fno = fileno(gi->GeoIPDatabase);
+
+ _check_mtime(gi);
+
+ /* first get past the database structure information */
+ for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
+ if (pread(fno, buf, 3, offset) != 3) {
+ return NULL;
+ }
+ offset += 3;
+ if (buf[0] == 255 && buf[1] == 255 && buf[2] == 255) {
+ has_structure_info = 1;
+ break;
+ }
+ offset -= 4;
+ if (offset < 0) {
+ return NULL;
+ }
+ }
+ if (has_structure_info) {
+ offset -= 6;
+ if (offset < 0) {
+ return NULL;
+ }
+ } else {
+ /* no structure info, must be pre Sep 2002 database, go back to end */
+ offset = gi->size - 3;
+ if (offset < 0) {
+ return NULL;
+ }
+ }
+
+ for (i = 0; i < DATABASE_INFO_MAX_SIZE; i++) {
+ if (pread(fno, buf, 3, offset) != 3) {
+ return NULL;
+ }
+ offset += 3;
+ if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0) {
+ retval = malloc(sizeof(char) * (i + 1));
+ if (retval == NULL) {
+ return NULL;
+ }
+ if (pread(fno, retval, i, offset) != i) {
+ return NULL;
+ }
+ retval[i] = '\0';
+ return retval;
+ }
+ offset -= 4;
+ if (offset < 0) {
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+/* GeoIP Region Edition functions */
+
+void GeoIP_assign_region_by_inetaddr_gl(GeoIP *gi,
+ unsigned long inetaddr,
+ GeoIPRegion *region,
+ GeoIPLookup *gl) {
+ unsigned int seek_region;
+
+ /* This also writes in the terminating NULs (if you decide to
+ * keep them) and clear any fields that are not set. */
+ memset(region, 0, sizeof(GeoIPRegion));
+
+ seek_region = _GeoIP_seek_record_gl(gi, ntohl(inetaddr), gl);
+
+ if (gi->databaseType == GEOIP_REGION_EDITION_REV0) {
+ /* Region Edition, pre June 2003 */
+ seek_region -= STATE_BEGIN_REV0;
+ if (seek_region >= 1000) {
+ region->country_code[0] = 'U';
+ region->country_code[1] = 'S';
+ region->region[0] = (char)((seek_region - 1000) / 26 + 65);
+ region->region[1] = (char)((seek_region - 1000) % 26 + 65);
+ } else {
+ const char *code = GeoIP_code_by_id(seek_region);
+ if (code != NULL) {
+ memcpy(region->country_code, code, 2);
+ }
+ }
+ } else if (gi->databaseType == GEOIP_REGION_EDITION_REV1) {
+ /* Region Edition, post June 2003 */
+ seek_region -= STATE_BEGIN_REV1;
+ if (seek_region < US_OFFSET) {
+ /* Unknown */
+ /* we don't need to do anything here b/c we memset region to 0 */
+ } else if (seek_region < CANADA_OFFSET) {
+ /* USA State */
+ region->country_code[0] = 'U';
+ region->country_code[1] = 'S';
+ region->region[0] = (char)((seek_region - US_OFFSET) / 26 + 65);
+ region->region[1] = (char)((seek_region - US_OFFSET) % 26 + 65);
+ } else if (seek_region < WORLD_OFFSET) {
+ /* Canada Province */
+ region->country_code[0] = 'C';
+ region->country_code[1] = 'A';
+ region->region[0] = (char)((seek_region - CANADA_OFFSET) / 26 + 65);
+ region->region[1] = (char)((seek_region - CANADA_OFFSET) % 26 + 65);
+ } else {
+ /* Not US or Canada ( cc_id is always cc_id * FIPS_RANGE ) */
+ const char *code =
+ GeoIP_code_by_id((seek_region - WORLD_OFFSET) / FIPS_RANGE);
+ if (code != NULL) {
+ /* coverity[dont_call] */
+ memcpy(region->country_code, code, 2);
+ }
+ }
+ }
+}
+
+void GeoIP_assign_region_by_inetaddr_v6_gl(GeoIP *gi,
+ geoipv6_t inetaddr,
+ GeoIPRegion *region,
+ GeoIPLookup *gl) {
+ unsigned int seek_region;
+
+ /* This also writes in the terminating NULs (if you decide to
+ * keep them) and clear any fields that are not set. */
+ memset(region, 0, sizeof(GeoIPRegion));
+
+ seek_region = _GeoIP_seek_record_v6_gl(gi, inetaddr, gl);
+
+ if (gi->databaseType == GEOIP_REGION_EDITION_REV0) {
+ /* Region Edition, pre June 2003 */
+ seek_region -= STATE_BEGIN_REV0;
+ if (seek_region >= 1000) {
+ region->country_code[0] = 'U';
+ region->country_code[1] = 'S';
+ region->region[0] = (char)((seek_region - 1000) / 26 + 65);
+ region->region[1] = (char)((seek_region - 1000) % 26 + 65);
+ } else {
+ const char *code = GeoIP_code_by_id(seek_region);
+ if (code != NULL) {
+ /* coverity[dont_call] */
+ memcpy(region->country_code, code, 2);
+ }
+ }
+ } else if (gi->databaseType == GEOIP_REGION_EDITION_REV1) {
+ /* Region Edition, post June 2003 */
+ seek_region -= STATE_BEGIN_REV1;
+ if (seek_region < US_OFFSET) {
+ /* Unknown */
+ /* we don't need to do anything here b/c we memset region to 0 */
+ } else if (seek_region < CANADA_OFFSET) {
+ /* USA State */
+ region->country_code[0] = 'U';
+ region->country_code[1] = 'S';
+ region->region[0] = (char)((seek_region - US_OFFSET) / 26 + 65);
+ region->region[1] = (char)((seek_region - US_OFFSET) % 26 + 65);
+ } else if (seek_region < WORLD_OFFSET) {
+ /* Canada Province */
+ region->country_code[0] = 'C';
+ region->country_code[1] = 'A';
+ region->region[0] = (char)((seek_region - CANADA_OFFSET) / 26 + 65);
+ region->region[1] = (char)((seek_region - CANADA_OFFSET) % 26 + 65);
+ } else {
+ /* Not US or Canada ( cc_id is always cc_id * FIPS_RANGE ) */
+ const char *code =
+ GeoIP_code_by_id((seek_region - WORLD_OFFSET) / FIPS_RANGE);
+ if (code != NULL) {
+ /* coverity[dont_call] */
+ memcpy(region->country_code, code, 2);
+ }
+ }
+ }
+}
+
+static GeoIPRegion *
+_get_region_gl(GeoIP *gi, unsigned long ipnum, GeoIPLookup *gl) {
+ GeoIPRegion *region;
+
+ region = malloc(sizeof(GeoIPRegion));
+ if (region) {
+ GeoIP_assign_region_by_inetaddr_gl(gi, htonl(ipnum), region, gl);
+ }
+ return region;
+}
+
+static GeoIPRegion *
+_get_region_v6_gl(GeoIP *gi, geoipv6_t ipnum, GeoIPLookup *gl) {
+ GeoIPRegion *region;
+
+ region = malloc(sizeof(GeoIPRegion));
+ if (region) {
+ GeoIP_assign_region_by_inetaddr_v6_gl(gi, ipnum, region, gl);
+ }
+ return region;
+}
+
+GeoIPRegion *
+GeoIP_region_by_addr_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ unsigned long ipnum;
+ if (addr == NULL) {
+ return NULL;
+ }
+ if (gi->databaseType != GEOIP_REGION_EDITION_REV0 &&
+ gi->databaseType != GEOIP_REGION_EDITION_REV1) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_REGION_EDITION_REV1));
+ return NULL;
+ }
+ ipnum = GeoIP_addr_to_num(addr);
+ return _get_region_gl(gi, ipnum, gl);
+}
+
+GeoIPRegion *
+GeoIP_region_by_addr_v6_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ geoipv6_t ipnum;
+ if (addr == NULL) {
+ return NULL;
+ }
+ if (gi->databaseType != GEOIP_REGION_EDITION_REV0 &&
+ gi->databaseType != GEOIP_REGION_EDITION_REV1) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_REGION_EDITION_REV1));
+ return NULL;
+ }
+ ipnum = _GeoIP_addr_to_num_v6(addr);
+ return _get_region_v6_gl(gi, ipnum, gl);
+}
+
+GeoIPRegion *
+GeoIP_region_by_name_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ unsigned long ipnum;
+ if (name == NULL) {
+ return NULL;
+ }
+ if (gi->databaseType != GEOIP_REGION_EDITION_REV0 &&
+ gi->databaseType != GEOIP_REGION_EDITION_REV1) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_REGION_EDITION_REV1));
+ return NULL;
+ }
+ if (!(ipnum = _GeoIP_lookupaddress(name))) {
+ return NULL;
+ }
+ return _get_region_gl(gi, ipnum, gl);
+}
+
+GeoIPRegion *
+GeoIP_region_by_name_v6_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ geoipv6_t ipnum;
+ if (name == NULL) {
+ return NULL;
+ }
+ if (gi->databaseType != GEOIP_REGION_EDITION_REV0 &&
+ gi->databaseType != GEOIP_REGION_EDITION_REV1) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_REGION_EDITION_REV1));
+ return NULL;
+ }
+
+ ipnum = _GeoIP_lookupaddress_v6(name);
+ if (__GEOIP_V6_IS_NULL(ipnum)) {
+ return NULL;
+ }
+ return _get_region_v6_gl(gi, ipnum, gl);
+}
+
+GeoIPRegion *
+GeoIP_region_by_ipnum_gl(GeoIP *gi, unsigned long ipnum, GeoIPLookup *gl) {
+ if (gi->databaseType != GEOIP_REGION_EDITION_REV0 &&
+ gi->databaseType != GEOIP_REGION_EDITION_REV1) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_REGION_EDITION_REV1));
+ return NULL;
+ }
+ return _get_region_gl(gi, ipnum, gl);
+}
+
+GeoIPRegion *
+GeoIP_region_by_ipnum_v6_gl(GeoIP *gi, geoipv6_t ipnum, GeoIPLookup *gl) {
+ if (gi->databaseType != GEOIP_REGION_EDITION_REV0 &&
+ gi->databaseType != GEOIP_REGION_EDITION_REV1) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_REGION_EDITION_REV1));
+ return NULL;
+ }
+ return _get_region_v6_gl(gi, ipnum, gl);
+}
+
+void GeoIPRegion_delete(GeoIPRegion *gir) { free(gir); }
+
+/* GeoIP Organization, ISP and AS Number Edition private method */
+static char *_get_name_gl(GeoIP *gi, unsigned long ipnum, GeoIPLookup *gl) {
+ unsigned int seek_org;
+ char buf[MAX_ORG_RECORD_LENGTH];
+ char *org_buf, *buf_pointer;
+ int record_pointer;
+ size_t len;
+
+ if (gi->databaseType != GEOIP_ORG_EDITION &&
+ gi->databaseType != GEOIP_ISP_EDITION &&
+ gi->databaseType != GEOIP_DOMAIN_EDITION &&
+ gi->databaseType != GEOIP_ASNUM_EDITION &&
+ gi->databaseType != GEOIP_ACCURACYRADIUS_EDITION &&
+ gi->databaseType != GEOIP_NETSPEED_EDITION_REV1 &&
+ gi->databaseType != GEOIP_USERTYPE_EDITION &&
+ gi->databaseType != GEOIP_REGISTRAR_EDITION &&
+ gi->databaseType != GEOIP_LOCATIONA_EDITION &&
+ gi->databaseType != GEOIP_CITYCONF_EDITION &&
+ gi->databaseType != GEOIP_COUNTRYCONF_EDITION &&
+ gi->databaseType != GEOIP_REGIONCONF_EDITION &&
+ gi->databaseType != GEOIP_POSTALCONF_EDITION) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_ORG_EDITION));
+ return NULL;
+ }
+
+ seek_org = _GeoIP_seek_record_gl(gi, ipnum, gl);
+ if (seek_org == gi->databaseSegments[0]) {
+ return NULL;
+ }
+
+ record_pointer =
+ seek_org + (2 * gi->record_length - 1) * gi->databaseSegments[0];
+
+ if (gi->cache == NULL) {
+ if (pread(fileno(gi->GeoIPDatabase),
+ buf,
+ MAX_ORG_RECORD_LENGTH,
+ record_pointer) == -1) {
+ return NULL;
+ }
+ if (gi->charset == GEOIP_CHARSET_UTF8) {
+ org_buf = _GeoIP_iso_8859_1__utf8((const char *)buf);
+ } else {
+ len = sizeof(char) * (strlen(buf) + 1);
+ org_buf = malloc(len);
+ strncpy(org_buf, buf, len);
+ }
+ } else {
+ buf_pointer = (char *)(gi->cache + (long)record_pointer);
+ if (gi->charset == GEOIP_CHARSET_UTF8) {
+ org_buf = _GeoIP_iso_8859_1__utf8((const char *)buf_pointer);
+ } else {
+ len = sizeof(char) * (strlen(buf_pointer) + 1);
+ org_buf = malloc(len);
+ strncpy(org_buf, buf_pointer, len);
+ }
+ }
+ return org_buf;
+}
+
+static char *_get_name_v6_gl(GeoIP *gi, geoipv6_t ipnum, GeoIPLookup *gl) {
+ unsigned int seek_org;
+ char buf[MAX_ORG_RECORD_LENGTH + 1];
+ char *org_buf, *buf_pointer;
+ int record_pointer;
+ size_t len;
+
+ if (gi->databaseType != GEOIP_ORG_EDITION_V6 &&
+ gi->databaseType != GEOIP_ISP_EDITION_V6 &&
+ gi->databaseType != GEOIP_DOMAIN_EDITION_V6 &&
+ gi->databaseType != GEOIP_ASNUM_EDITION_V6 &&
+ gi->databaseType != GEOIP_ACCURACYRADIUS_EDITION_V6 &&
+ gi->databaseType != GEOIP_NETSPEED_EDITION_REV1_V6 &&
+ gi->databaseType != GEOIP_USERTYPE_EDITION_V6 &&
+ gi->databaseType != GEOIP_REGISTRAR_EDITION_V6 &&
+ gi->databaseType != GEOIP_LOCATIONA_EDITION_V6) {
+ printf("Invalid database type %s, expected %s\n",
+ get_db_description(gi->databaseType),
+ get_db_description(GEOIP_ORG_EDITION));
+ return NULL;
+ }
+
+ seek_org = _GeoIP_seek_record_v6_gl(gi, ipnum, gl);
+ if (seek_org == gi->databaseSegments[0]) {
+ return NULL;
+ }
+
+ record_pointer =
+ seek_org + (2 * gi->record_length - 1) * gi->databaseSegments[0];
+
+ if (gi->cache == NULL) {
+ if (pread(fileno(gi->GeoIPDatabase),
+ buf,
+ MAX_ORG_RECORD_LENGTH,
+ record_pointer) == -1) {
+ return NULL;
+ }
+ buf[MAX_ORG_RECORD_LENGTH] = 0;
+ if (gi->charset == GEOIP_CHARSET_UTF8) {
+ org_buf = _GeoIP_iso_8859_1__utf8((const char *)buf);
+ } else {
+ len = sizeof(char) * (strlen(buf) + 1);
+ org_buf = malloc(len);
+ strncpy(org_buf, buf, len);
+ }
+ } else {
+ buf_pointer = (char *)(gi->cache + (long)record_pointer);
+ if (gi->charset == GEOIP_CHARSET_UTF8) {
+ org_buf = _GeoIP_iso_8859_1__utf8((const char *)buf_pointer);
+ } else {
+ len = sizeof(char) * (strlen(buf_pointer) + 1);
+ org_buf = malloc(len);
+ strncpy(org_buf, buf_pointer, len);
+ }
+ }
+ return org_buf;
+}
+
+char *GeoIP_num_to_addr(unsigned long ipnum) {
+ char *ret_str;
+ char *cur_str;
+ int octet[4];
+ int num_chars_written, i;
+
+ ret_str = malloc(sizeof(char) * 16);
+ cur_str = ret_str;
+
+ for (i = 0; i < 4; i++) {
+ octet[3 - i] = ipnum % 256;
+ ipnum >>= 8;
+ }
+
+ for (i = 0; i < 4; i++) {
+ num_chars_written = sprintf(cur_str, "%d", octet[i]);
+ cur_str += num_chars_written;
+
+ if (i < 3) {
+ cur_str[0] = '.';
+ cur_str++;
+ }
+ }
+
+ return ret_str;
+}
+
+char **GeoIP_range_by_ip_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ unsigned long ipnum;
+ unsigned long left_seek;
+ unsigned long right_seek;
+ unsigned long mask;
+ int orig_netmask;
+ unsigned int target_value;
+ char **ret;
+ GeoIPLookup t;
+
+ if (addr == NULL) {
+ return NULL;
+ }
+
+ ret = malloc(sizeof(char *) * 2);
+
+ ipnum = GeoIP_addr_to_num(addr);
+ target_value = _GeoIP_seek_record_gl(gi, ipnum, gl);
+ orig_netmask = gl->netmask;
+ mask = 0xffffffff << (32 - orig_netmask);
+ left_seek = ipnum & mask;
+ right_seek = left_seek + (0xffffffff & ~mask);
+
+ while (left_seek != 0 &&
+ target_value == _GeoIP_seek_record_gl(gi, left_seek - 1, &t)) {
+
+ /* Go to beginning of netblock defined by netmask */
+ mask = 0xffffffff << (32 - t.netmask);
+ left_seek = (left_seek - 1) & mask;
+ }
+ ret[0] = GeoIP_num_to_addr(left_seek);
+
+ while (right_seek != 0xffffffff &&
+ target_value == _GeoIP_seek_record_gl(gi, right_seek + 1, &t)) {
+
+ /* Go to end of netblock defined by netmask */
+ mask = 0xffffffff << (32 - t.netmask);
+ right_seek = (right_seek + 1) & mask;
+ right_seek += 0xffffffff & ~mask;
+ }
+ ret[1] = GeoIP_num_to_addr(right_seek);
+
+ gi->netmask = orig_netmask;
+
+ return ret;
+}
+void GeoIP_range_by_ip_delete(char **ptr) {
+ if (ptr) {
+ if (ptr[0]) {
+ free(ptr[0]);
+ }
+ if (ptr[1]) {
+ free(ptr[1]);
+ }
+ free(ptr);
+ }
+}
+
+char *GeoIP_name_by_ipnum_gl(GeoIP *gi, unsigned long ipnum, GeoIPLookup *gl) {
+ return _get_name_gl(gi, ipnum, gl);
+}
+
+char *GeoIP_name_by_ipnum_v6_gl(GeoIP *gi, geoipv6_t ipnum, GeoIPLookup *gl) {
+ return _get_name_v6_gl(gi, ipnum, gl);
+}
+
+char *GeoIP_name_by_addr_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ unsigned long ipnum;
+ if (addr == NULL) {
+ return NULL;
+ }
+ ipnum = GeoIP_addr_to_num(addr);
+ return _get_name_gl(gi, ipnum, gl);
+}
+
+char *GeoIP_name_by_addr_v6_gl(GeoIP *gi, const char *addr, GeoIPLookup *gl) {
+ geoipv6_t ipnum;
+ if (addr == NULL) {
+ return NULL;
+ }
+ ipnum = _GeoIP_addr_to_num_v6(addr);
+ return _get_name_v6_gl(gi, ipnum, gl);
+}
+
+char *GeoIP_name_by_name_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ unsigned long ipnum;
+ if (name == NULL) {
+ return NULL;
+ }
+ if (!(ipnum = _GeoIP_lookupaddress(name))) {
+ return NULL;
+ }
+ return _get_name_gl(gi, ipnum, gl);
+}
+
+char *GeoIP_name_by_name_v6_gl(GeoIP *gi, const char *name, GeoIPLookup *gl) {
+ geoipv6_t ipnum;
+ if (name == NULL) {
+ return NULL;
+ }
+ ipnum = _GeoIP_lookupaddress_v6(name);
+ if (__GEOIP_V6_IS_NULL(ipnum)) {
+ return NULL;
+ }
+ return _get_name_v6_gl(gi, ipnum, gl);
+}
+
+unsigned char GeoIP_database_edition(GeoIP *gi) { return gi->databaseType; }
+
+int GeoIP_enable_teredo(GeoIP *gi, int true_false) {
+ unsigned int mask = (1U << GEOIP_TEREDO_BIT);
+ int b = (gi->ext_flags & mask) ? 1 : 0;
+ gi->ext_flags &= ~mask;
+ if (true_false) {
+ gi->ext_flags |= true_false;
+ }
+ return b;
+}
+
+int GeoIP_teredo(GeoIP *gi) {
+ unsigned int mask = (1U << GEOIP_TEREDO_BIT);
+ return (gi->ext_flags & mask) ? 1 : 0;
+}
+
+int GeoIP_charset(GeoIP *gi) { return gi->charset; }
+
+int GeoIP_set_charset(GeoIP *gi, int charset) {
+ int old_charset = gi->charset;
+ gi->charset = charset;
+ return old_charset;
+}
+
+/** return two letter country code */
+const char *GeoIP_code_by_id(int id) {
+ if (id < 0 || id >= (int)num_GeoIP_countries) {
+ return NULL;
+ }
+
+ return GeoIP_country_code[id];
+}
+
+/** return three letter country code */
+const char *GeoIP_code3_by_id(int id) {
+ if (id < 0 || id >= (int)num_GeoIP_countries) {
+ return NULL;
+ }
+
+ return GeoIP_country_code3[id];
+}
+
+/** return full name of country in utf8 or iso-8859-1 */
+const char *GeoIP_country_name_by_id(GeoIP *gi, int id) {
+ /* return NULL also even for index 0 for backward compatibility */
+ if (id <= 0 || id >= (int)num_GeoIP_countries) {
+ return NULL;
+ }
+ return (gi->charset == GEOIP_CHARSET_UTF8) ? GeoIP_utf8_country_name[id]
+ : GeoIP_country_name[id];
+}
+
+/** return full name of country in iso-8859-1 */
+const char *GeoIP_name_by_id(int id) {
+ if (id < 0 || id >= (int)num_GeoIP_countries) {
+ return NULL;
+ }
+
+ return GeoIP_country_name[id];
+}
+
+/** return continent of country */
+const char *GeoIP_continent_by_id(int id) {
+ if (id < 0 || id >= (int)num_GeoIP_countries) {
+ return NULL;
+ }
+
+ return GeoIP_country_continent[id];
+}
+
+/** return id by country code **/
+int GeoIP_id_by_code(const char *country) {
+ unsigned i;
+
+ for (i = 0; i < num_GeoIP_countries; ++i) {
+ if (strcmp(country, GeoIP_country_code[i]) == 0) {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+unsigned GeoIP_num_countries(void) { return num_GeoIP_countries; }
+
+const char *GeoIP_lib_version(void) { return PACKAGE_VERSION; }
+
+int GeoIP_cleanup(void) {
+ int i, result = 0;
+ char **tmpGeoIPDBFileName = GeoIPDBFileName;
+
+ GeoIPDBFileName = NULL;
+
+ if (tmpGeoIPDBFileName) {
+ for (i = 0; i < NUM_DB_TYPES; i++) {
+ if (tmpGeoIPDBFileName[i]) {
+ free(tmpGeoIPDBFileName[i]);
+ }
+ }
+
+ free(tmpGeoIPDBFileName);
+ result = 1;
+ }
+
+ return result;
+}