java.lang.Object
net.i2p.router.transport.GeoIP

public class GeoIPextends Object
Manage geoip lookup in a file with the Tor geoip format.

The lookup is expensive, so a lookup is queued with add(). The actual lookup of multiple IPs is fired with lookup(). To get a country for an IP, use get() which returns a lower-case, generally two-letter country code or null.

Everything here uses longs, since Java is signed-only, the file is sorted by unsigned, and we don't store the table in memory (unlike in Blocklist.java, where it's in-memory so we want to be space-efficient).

  • Field Details

    • _log

      private final Log _log
      Logger for the GeoIP lookups.
    • _context

      private final I2PAppContext _context
      The app context.
    • _banLogger

      private static volatile BanLogger _banLogger
      Records why routers were banned.
    • _codeToName

      private final Map<String,String> _codeToName
      Country names, by country code.
    • _codeCache

      private final Map<String,String> _codeCache
      Code to itself to prevent String proliferation.
    • _IPToCountry

      private final Map<Long,String> _IPToCountry
      Country codes, keyed by the IP: an IPv4 address as a non-negative long, the first 8 bytes of an IPv6 address as a signed long.
    • MAX_IP_CACHE_SIZE

      private static final int MAX_IP_CACHE_SIZE
      Cap for _IPToCountry to prevent unbounded growth.
      See Also:
    • _pendingSearch

      private final Set<Long> _pendingSearch
      IPv4 addresses queued for lookup.
    • _pendingIPv6Search

      private final Set<Long> _pendingIPv6Search
      IPv6 addresses queued for lookup.
    • _notFound

      private final Set<Long> _notFound
      Addresses searched already without a result.
    • _lock

      private final AtomicBoolean _lock
      True while a lookup run is in progress.
    • _lookupRunCount

      private int _lookupRunCount
      Lookups run in the current batch.
    • _associatedCountries

      private static final Map<String, List<String>> _associatedCountries
      Countries treated as one, by country code.
    • _asnReader

      private volatile Reader _asnReader
      Reader for the ASN to organization name lookups.
    • PROP_GEOIP_ENABLED

      static final String PROP_GEOIP_ENABLED
      Property: whether GeoIP lookups are enabled.
      See Also:
    • PROP_NORMALIZE_ISP

      static final String PROP_NORMALIZE_ISP
      Normalize ISP/org names from ASN database (strip suffixes, abbreviate, title-case).
      See Also:
    • PROP_GEOIP_DIR

      public static final String PROP_GEOIP_DIR
      Property: the directory the GeoIP databases are read from.
      See Also:
    • GEOIP_DIR_DEFAULT

      public static final String GEOIP_DIR_DEFAULT
      Default GeoIP directory, under the router's base directory.
      See Also:
    • GEOIP_FILE_DEFAULT

      static final String GEOIP_FILE_DEFAULT
      Default geoip file name.
      See Also:
    • GEOIP2_FILE_DEFAULT

      public static final String GEOIP2_FILE_DEFAULT
      GEOIP2_FILE_DEFAULT.
      See Also:
    • ASN_FILE_DEFAULT

      public static final String ASN_FILE_DEFAULT
      Default MaxMind ASN database file name.
      See Also:
    • COUNTRY_FILE_DEFAULT

      static final String COUNTRY_FILE_DEFAULT
      Default country names file name.
      See Also:
    • PROP_IP_COUNTRY

      public static final String PROP_IP_COUNTRY
      Property: the last country an IP was seen in, set for the console.
      See Also:
    • PROP_DEBIAN_GEOIP

      public static final String PROP_DEBIAN_GEOIP
      Property: the Debian-packaged IPv4 GeoIP database path.
      See Also:
    • PROP_DEBIAN_GEOIPV6

      public static final String PROP_DEBIAN_GEOIPV6
      PROP_DEBIAN_GEOIPV6.
      See Also:
    • DEBIAN_GEOIP_FILE

      private static final String DEBIAN_GEOIP_FILE
      Debian's IPv4 GeoIP database path.
      See Also:
    • DEBIAN_GEOIPV6_FILE

      private static final String DEBIAN_GEOIPV6_FILE
      Debian's IPv6 GeoIP database path.
      See Also:
    • DISABLE_DEBIAN

      private static final boolean DISABLE_DEBIAN
      Developer switch disabling the Debian databases.
      See Also:
    • ENABLE_DEBIAN

      private static final boolean ENABLE_DEBIAN
      SystemVersion.isAndroid()) True where the Debian databases are usable.
    • PROP_BLOCK_MY_COUNTRY

      public static final String PROP_BLOCK_MY_COUNTRY
      Property: whether to refuse tunnels to destinations in our own country.
      See Also:
    • UNKNOWN_COUNTRY_CODE

      private static final String UNKNOWN_COUNTRY_CODE
      Maxmind API.
      See Also:
    • UNKNOWN_COUNTRY_CODE2

      private static final String UNKNOWN_COUNTRY_CODE2
      Unknown code from db-ip.com (https://db-ip.com/faq.php).
      See Also:
    • ASN_DB_OVERRIDES

      private static final Map<String,String> ASN_DB_OVERRIDES
      Known broken org names in the MaxMind ASN DB — straight replacements.
    • KNOWN_WORDS

      private static final Set<String> KNOWN_WORDS
      Common org/internet words used to detect character-reversed names. If enough words in a name match their reversed form, the name is auto-reversed.
    • SUFFIX_ALL

      private static final Pattern SUFFIX_ALL
      Suffixes to strip from org names — single combined alternation (44→1).
    • KEEP_UPPER

      private static final Set<String> KEEP_UPPER
      Words to keep uppercase.
    • BRAND_NAMES

      private static final Map<String,String> BRAND_NAMES
      Brand names that should keep their exact casing.
    • VODAFONE_COUNTRIES

      private static final List<String> VODAFONE_COUNTRIES
      Vodafone subsidiaries: "Vodafone <Country>" names, longest first so a multi-word country matches before a shorter prefix of it.
    • VODAFONE_ALIASES

      private static final Map<String,String> VODAFONE_ALIASES
      Vodafone regional brand aliases that are not country names.
    • TRAILING_VERBOSE

      private static final Pattern TRAILING_VERBOSE
      Trailing generic words to drop (redundant suffixes like "Communications", "Services").
    • CHINA_VERBOSE

      private static final Pattern CHINA_VERBOSE
      China carrier network labels and region designators to drop (China169 backbone, IP Network, Province, Municipality, Autonomous Region); leftover spaces are collapsed by the DOUBLE_SPACE step below.
    • ABBREV_MAP

      private static final Map<String,String> ABBREV_MAP
      Abbreviation map for verbose words.
    • ABBREV_ALL

      private static final Pattern ABBREV_ALL
      Single combined abbreviation pattern.
    • FILLER_DROP

      private static final Pattern FILLER_DROP
      Filler prepositions/articles to drop.
    • ASN_EMBEDDED

      private static final Pattern ASN_EMBEDDED
      Embedded ASN numbers (e.g. AS12345).
    • ASN_TRAILING

      private static final Pattern ASN_TRAILING
      Matches a trailing AS number in an organization name.
    • PAIRED_SINGLE_QUOTES

      private static final Pattern PAIRED_SINGLE_QUOTES
      Paired single quotes around text (e.g. 'TIMEWEB') — keeps internal apostrophes like O'Brien.
    • PAREN_ALL

      private static final Pattern PAREN_ALL
      Parentheticals to strip (anywhere, not just trailing).
    • DASH_SPACE_DASH

      private static final Pattern DASH_SPACE_DASH
      Normalize mangled dash separators: "hosted- -vdsina" → "hosted - vdsina".
    • LEADING_JUNK

      private static final Pattern LEADING_JUNK
      Leading/trailing junk.
    • TRAILING_JUNK

      private static final Pattern TRAILING_JUNK
      Matches trailing punctuation in an organization name.
    • DOUBLE_COMMA

      private static final Pattern DOUBLE_COMMA
      Matches doubled commas in an organization name.
    • DOUBLE_SPACE

      private static final Pattern DOUBLE_SPACE
      Matches doubled spaces in an organization name.
    • SPACE_SPLIT

      private static final Pattern SPACE_SPLIT
      Whitespace split.
    • LEADING_TRAILING_JUNK

      private static final Pattern LEADING_TRAILING_JUNK
      Leading/trailing punctuation trim.
    • STRIP_PREFIX_ALL

      private static final Pattern STRIP_PREFIX_ALL
      Verbose prefixes to strip — single combined alternation (3→1).
    • FILLER_WORDS

      private static final Pattern FILLER_WORDS
      Filler words that can be trimmed from long names.
    • ACRONYM_PATTERN

      private static final Pattern ACRONYM_PATTERN
      Precompiled pattern for short all-caps acronym detection.
    • SKIP_ALL

      private static final Pattern SKIP_ALL
      Names that should be returned as-is — single combined pattern vs array.
    • _GEOIP_DATE_FORMAT

      private static final ThreadLocal<DateFormat> _GEOIP_DATE_FORMAT
      Formats a database build date for the console, e.g. "Oct 22, 2026". One formatter per thread, as SimpleDateFormat is not thread-safe.
  • Constructor Details

    • GeoIP

      public GeoIP(I2PAppContext context)
      GeoIP lookup service for IP to country mapping.
      Parameters:
      context - RouterContext in production, I2PAppContext for testing only
  • Method Details

    • shutdown

      public void shutdown()
      Shutdown and release resources.
    • blockingLookup

      public void blockingLookup()
      Blocking lookup of all pending IPs. Results will be added to the table and available via get() after completion.

      Public for BundleRouterInfos.

    • countryToIP

      private void countryToIP(String country)
      Write all IP ranges for country to blocklist-country.txt. Inline, blocking.
      Parameters:
      country - lower-case two-letter country code
    • getGeoIP2

      private File getGeoIP2()
      The GeoIP2 database file to use.
      Returns:
      null if not found
    • getASN

      private File getASN()
      The ASN database file to use.
      Returns:
      null if not found
    • openASN

      private Reader openASN() throws IOException
      Open the ASN database reader (lazy, cached).
      Returns:
      the reader for the ASN database, or null if the database file is missing
      Throws:
      IOException - if the file exists but cannot be read as a MaxMind database
    • getOrgName

      public String getOrgName(String ipAddress)
      The organization name for an IP from the local ASN database. Returns the AS organization (e.g. "Google LLC") or null if not found.
      Parameters:
      ipAddress - IPv4 or IPv6 address string
      Returns:
      org name or null
    • reverseWord

      private static String reverseWord(String word)
      Reverse a single word.
      Parameters:
      word - the characters to reverse
      Returns:
      the same characters in the opposite order
    • applyAbbreviation

      private static String applyAbbreviation(String name)
      Single-pass abbreviation replacement (was 29× replaceAll).
      Parameters:
      name - the organisation name to shorten
      Returns:
      the name with every verbose-word match replaced by its abbreviation
    • fixReversedWords

      private static String fixReversedWords(String name)
      Detect and fix character-reversed words from MaxMind ASN DB. e.g. "eteicoS esiacnarF uD enohpeletoidaR" -> "Societe France Du RadioTelephone"

      Detection passes: 1. Word-by-word: words starting lowercase and ending uppercase are likely reversed. 2. Full-string: try reversing the entire name for compound words without spaces. 3. First/last char case-swap: turns "techteL" back into "Techtel". 4. Full word-level char reversal: reverse every word unconditionally, catching all-lowercase reversed words like "enofadov" where decode isn't in known-words. 5. Word-order reversal: flip the word order (catches "loclix as trading..."). 6. Combined: char-reversal then word-order, catching both at once, as in "tsohxuled sa gnidart inolletram oettam". Also detects "as trading" pattern which strongly signals word-order is reversed. Picks whichever pass scores highest (known-word matches + title-case words).

      AS numbers must be stripped before calling this (see normalizeOrgName).

      Parameters:
      name - the organisation name as read from the database
      Returns:
      the corrected name, or the input unchanged if no reversal was detected
    • scoreCandidate

      private static int scoreCandidate(String text)
      Score a candidate by known-word matches (weighted 3x) plus title-case word count. Title-case words are a strong signal because ASN org names are stored title-cased, and reversed names are all-lowercase.
      Parameters:
      text - the candidate name to score
      Returns:
      the score, or -1 for an empty candidate so that it always loses
    • countTitleCaseWords

      private static int countTitleCaseWords(String text)
      Count words starting with uppercase (title-case signal for proper org names).
      Parameters:
      text - the name to scan
      Returns:
      how many space-separated words of two or more characters start with an uppercase letter
    • tryFixAsTrading

      private static String tryFixAsTrading(String name, String allWordReversed)
      Detect "as trading" pattern (lowercase), a strong signal that word order is reversed. A correct org name has "Trading as X", but word-order reversal produces "X as trading". Also checks the char-reversed candidate for the same pattern.
      Parameters:
      name - the original organisation name
      allWordReversed - the candidate with every word char-reversed, may be empty
      Returns:
      the reordered name when the "as trading" pattern is present, otherwise ""
    • tryWordOrderIfHasAsTrading

      private static String tryWordOrderIfHasAsTrading(String text)
      If text contains "as trading", reverse word order and check for "trading as".
      Parameters:
      text - the candidate name to test
      Returns:
      the reordered name if it now reads "trading as", otherwise ""
    • tryWordReversal

      private static String tryWordReversal(String[] words)
      Try reversing individual words that look reversed (lowercase start, uppercase end).
      Parameters:
      words - the name split on spaces
      Returns:
      the name with the reversed-looking words flipped, "" if none look reversed
    • tryCaseSwap

      private static String tryCaseSwap(String[] words)
      Try swapping the case of first and last characters in each word. Handles MaxMind corruption where "Techtel" becomes "techteL", with the first and last characters' case swapped. Only acts if at least one word has the corruption signature, a lowercase start with an uppercase end; words that don't match the pattern are left unchanged.
      Parameters:
      words - the name split on spaces
      Returns:
      the name with the corrupted words' case swapped, "" if none are corrupted
    • swapFirstLastCase

      private static String swapFirstLastCase(String word)
      Swap the case of the first and last characters of a word.
      Parameters:
      word - the word to swap; returned as-is if shorter than two characters
      Returns:
      the word with its first and last characters' case flipped
    • tryFullStringReversal

      private static String tryFullStringReversal(String name)
      Try reversing the entire string as one block (for compound words).
      Parameters:
      name - the organisation name to reverse as a whole
      Returns:
      the reversed name if some word of it starts with an uppercase letter, otherwise ""
    • tryFullWordReversal

      private static String tryFullWordReversal(String[] words)
      Reverse every word at character level unconditionally. Catches all-lowercase reversed words like "enofadov" where the decoded form ("vodafone") isn't in the known-words list, so case-pattern detection wouldn't trigger.
      Parameters:
      words - the name split on spaces
      Returns:
      every word reversed, in the original word order
    • tryWordOrderReversal

      private static String tryWordOrderReversal(String name)
      Reverse the order of words. Catches word-order reversal like "loclix as trading sciacca filiberto matteo" → "matteo filiberto sciacca trading as loclix".
      Parameters:
      name - the organisation name to reorder
      Returns:
      the words in reverse order, "" if there are fewer than two words
    • countKnownWordMatches

      private static int countKnownWordMatches(String text)
      Count how many space-separated words match known words.
      Parameters:
      text - the name to scan
      Returns:
      how many of its words are in the known-words set, ignoring case
    • looksReversed

      private static boolean looksReversed(String word)
      A word looks reversed if: 1. Its first letter is lowercase and last letter is uppercase (e.g. "puorG" → "Group"), OR 2. Reversing its letters yields a known word (e.g. "CSJP" → "PJSC").

      Leading/trailing non-letter characters are ignored for the letter check so that "talasite(" and ")puorG" are correctly detected.

      Parameters:
      word - the word to test
      Returns:
      true if the word's case pattern, or the word its letters decode to when reversed, suggests it was stored reversed
    • normalizeOrgName

      public static String normalizeOrgName(String raw)
      Normalize an ISP/org name from the ASN database. Strips suffixes, abbreviates verbose words, title-cases with acronym preservation.
      Parameters:
      raw - original org name from MMDB
      Returns:
      the normalized organisation name, or the input if it was null or empty
    • collapseVodafoneCountry

      private static String collapseVodafoneCountry(String name)
      Collapse "Vodafone <Country> ..." names, with corporate-suffix cruft from truncated MaxMind entries, to just "Vodafone <Country>". Country matching is multi-word aware ("Czech Republic", "New Zealand"). Non-country Vodafone names (Group, Idea, etc.) pass through unchanged.
      Parameters:
      name - the already-normalized organisation name
      Returns:
      collapsed name if a known country was matched, else the input
    • firstWord

      private static String firstWord(String s)
      First whitespace-delimited word of a string with trailing punctuation stripped, or the whole string.
      Parameters:
      s - the string to take the first word of
      Returns:
      the text up to the first space, without trailing non-alphanumerics
    • titleCasePreserve

      private static String titleCasePreserve(String name)
      Title-case a name while preserving acronyms and brand names.
      Parameters:
      name - the name to re-case, split on spaces
      Returns:
      the re-cased name, with brand names, short all-caps acronyms and KEEP_UPPER words left as they are
    • openGeoIP2

      private DatabaseReader openGeoIP2(File geoFile) throws IOException
      Open a GeoIP2 database.
      Parameters:
      geoFile - the GeoIP2 database file to open
      Returns:
      a reader with a 256-entry cache, whose build date is also reported to the update manager
      Throws:
      IOException - if the file cannot be read, or is not a valid MaxMind database
    • getGeoIPBuildInfo

      public String getGeoIPBuildInfo()
      Return the country database's build date, size and path, for the console.
      Returns:
      the HTML build info, or a not-found or error message
    • getASNBuildInfo

      public String getASNBuildInfo()
      Return the current ASN database version.
      Returns:
      the HTML build info, or a not-found or error message
    • readCountryFile

      private void readCountryFile()
      Read in and parse the country file. The file need not be sorted.

      Acceptable formats: #comment (# must be in column 1) code,full name

      Example: US,UNITED STATES

      To create: wget https://web.archive.org/web/2010id_/http://ip-to-country.webhosting.info/downloads/ip-to-country.csv.zip unzip ip-to-country.csv.zip cut -d, -f3,5 < ip-to-country.csv|sed 's/"//g' | sort | uniq > countries.txt.

    • readGeoIPFile

      private String[] readGeoIPFile(Long[] search)
      Read in and parse the geoip file. The geoip file must be sorted, and may not contain overlapping entries.

      Acceptable formats (IPV4 only): #comment (# must be in column 1) integer IP,integer IP, country code

      Example: 121195296,121195327,IT

      This is identical to the Tor geoip file, which can be found in src/config/geoip in their distribution, or /usr/local/lib/share/tor/geoip in their installation.

      To create: wget https://web.archive.org/web/2010id_/http://ip-to-country.webhosting.info/downloads/ip-to-country.csv.zip unzip ip-to-country.csv.zip cut -d, -f0-3 < ip-to-country.csv|sed 's/"//g' > geoip.txt.

      Parameters:
      search - a sorted array of IPs to search
      Returns:
      an array of country codes, same order as the search param, with null for an IP outside every range in the file; a zero-length array if the file is missing
    • notifyVersion

      private void notifyVersion(String subtype, long version)
      Tell the update manager.
      Parameters:
      subtype - the updater subtype recorded against the GEOIP update type
      version - the version number to report; zero or below is ignored
    • notifyVersion

      static void notifyVersion(I2PAppContext ctx, String subtype, long version)
      Tell the update manager.
      Parameters:
      ctx - the application context whose client app manager holds the registered update manager
      subtype - the updater subtype recorded against the GEOIP update type, e.g. "GeoIP"
      version - the version number being reported; anything zero or below is ignored
    • notifyVersion

      static void notifyVersion(I2PAppContext ctx, String subtype, String version)
      Tell the update manager, with a version string as-is, e.g. the product version embedded in a database's metadata.
      Parameters:
      ctx - the application context whose client app manager holds the registered update manager
      subtype - the updater subtype recorded against the GEOIP update type, e.g. "ASN"
      version - the version being reported; null or empty is ignored
    • updateOurCountry

      private void updateOurCountry(long ts)
      Put our country code in the config, where others (such as Timestamper) can get it, and it will be there next time at startup.

      Does nothing in I2PAppContext.

      Parameters:
      ts - the timestamp of the geoip file that was read, greater than zero
    • banCountry

      public static void banCountry(RouterContext ctx, String country)
      Bans routers located in a specified country.

      This method iterates through all known routers in the network database and adds those located in the specified country to the banlist. Routers are banned indefinitely with a reason message that reflects whether the application is in 'block my country' mode or 'hidden' mode.

      Parameters:
      ctx - the RouterContext instance providing access to network database and banlist
      country - the country code (ISO 3166-1 alpha-2 format) of the country whose routers should be banned
    • add

      public void add(String ip)
      Add to the list needing lookup Public for BundleRouterInfos.
      Parameters:
      ip - IPv4 or IPv6
    • add

      public void add(byte[] ip)
      Add to the list needing lookup Public for BundleRouterInfos.
      Parameters:
      ip - IPv4 or IPv6
    • add

      private void add(long ip)
      See above for ip-to-long mapping.
      Parameters:
      ip - the address as a long, IPv4 in the low 32 bits, IPv6 as the first 8 bytes
    • get

      public String get(String ip)
      The country for an IP from the cache. Public for BundleRouterInfos.
      Parameters:
      ip - IPv4 or IPv6
      Returns:
      lower-case code, generally two letters, or null.
    • get

      String get(byte[] ip)
      The country for an IP from the cache.
      Parameters:
      ip - IPv4 or IPv6
      Returns:
      lower-case code, generally two letters, or null.
    • get

      private String get(long ip)
      The country for an IP from the cache. See above for ip-to-long mapping.
      Parameters:
      ip - the address as a long, IPv4 in the low 32 bits, IPv6 as the first 8 bytes
      Returns:
      lower-case code, generally two letters, or null if not cached
    • toLong

      private static long toLong(byte[] ip)
      Convert an address to the long form used as the cache key. See above for ip-to-long mapping.
      Parameters:
      ip - four bytes for IPv4, sixteen bytes for IPv6
      Returns:
      IPv4 as a non-negative long, IPv6 as the first 8 bytes as a signed long
    • toV4

      private static String toV4(long ip)
      The given IPv4 as a dotted-quad string.
      Parameters:
      ip - the IPv4 address as a non-negative long
      Returns:
      the address in dotted-quad form, for example 1.2.3.4
    • toV6

      private static String toV6(long ip)
      The given IPv6 as a hex string.
      Parameters:
      ip - the first 8 bytes of the IPv6 address as a long
      Returns:
      the address in hex form, for example aabb:ccdd:eeff:1122::
    • fullName

      public String fullName(String code)
      The untranslated country name for a country code. Public for BundleRouterInfos.
      Parameters:
      code - two-letter lower case code
      Returns:
      untranslated name or null
    • getCountries

      public Map<String,String> getCountries()
      The country code map.
      Returns:
      Map of two-letter lower case code to untranslated country name, unmodifiable