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

public class CountryLookupextends Object
Country-code lookup, caching, and strict-country policy for the router.

Extracted from CommSystemFacadeImpl (2026-09-06) so the country cluster - a per-peer country cache with periodic cleanup, over-capacity eviction, strict-country gating, and startup GeoIP queueing - lives apart from the facade core. The facade retains thin delegation stubs so the public static and commSystem() APIs are unchanged.

All pure decision logic (cache eviction decisions, memory-independent helpers) is exposed as package-visible static methods so it can be tested without a running router; see CountryLookupDecisionTest.

  • Field Details

    • _context

      private final RouterContext _context
      The router context.
    • _geoIP

      private final GeoIP _geoIP
      The GeoIP databases.
    • _log

      private final Log _log
      Logger for the country lookup.
    • START_DELAY

      private static final int START_DELAY
      Delay before the first lookup run, hoping the router infos are read in by then, though that is not required.
    • LOOKUP_TIME

      private static final int LOOKUP_TIME
      Time allowed for one lookup run.
      See Also:
    • QUEUE_TIME

      private static final int QUEUE_TIME
      Interval between the lookup runs, re-queueing the whole net DB so peers learned after startup are resolved even if their page is never viewed explicitly.
      See Also:
    • MAX_COUNTRY_CACHE_SIZE

      private static final int MAX_COUNTRY_CACHE_SIZE
      Most routers cached with a country.
      See Also:
    • COUNTRY_CACHE_EXPIRY

      private static final long COUNTRY_CACHE_EXPIRY
      Age after which a cached country expires.
      See Also:
    • lastUnknownPurge

      private long lastUnknownPurge
      When the unknown-country entries were last purged.
    • lastCacheCleanup

      private long lastCacheCleanup
      When the country cache was last cleaned up.
    • countryCache

      private final ConcurrentHashMap<Hash,String> countryCache
      The cached countries, by router hash.
    • countryCacheTimestamps

      private final ConcurrentHashMap<Hash,Long> countryCacheTimestamps
      When each cached country was stored.
    • DOT_SPLIT

      private static final Pattern DOT_SPLIT
      Pre-compiled dot split for domain parsing.
    • MULTI_PART_TLDS

      private static final Set<String> MULTI_PART_TLDS
      Multi-part TLD list for domain extraction.
  • Constructor Details

    • CountryLookup

      public CountryLookup(RouterContext ctx, GeoIP geoIP)
      Create a lookup over the netDb's router IPs, backed by the GeoIP database.
      Parameters:
      ctx - non-null
      geoIP - non-null
  • Method Details

    • start

      public void start()
      Collect the IPs for all routers in the DB and queue them for lookup. The recurring lookup pipeline starts on the first pass only.
    • queueLookup

      public void queueLookup(byte[] ip)
      Queues a GeoIP lookup for the given IP.
      Parameters:
      ip - ipv4 or ipv6
    • getOurCountry

      public String getOurCountry()
      Country code for this router, from the GeoIP lookup.
      Returns:
      two-letter lower-case country code or null
    • isInStrictCountry

      public boolean isInStrictCountry()
      Are we in a strict country.
      Returns:
      whether in strict country
    • isInStrictCountry

      public boolean isInStrictCountry(Hash peer)
      Are they in a strict country. Not recommended for our local router hash, as we may not be either in the cache or netdb, or may not be publishing an IP.
      Parameters:
      peer - non-null
      Returns:
      whether in strict country
    • isInStrictCountry

      public boolean isInStrictCountry(RouterInfo ri)
      Are they in a strict country.
      Parameters:
      ri - non-null
      Returns:
      whether in strict country
    • getCountry

      public String getCountry(Hash peer)
      Uses the transport IP first because that lookup is fast, then the IP from the netDb. Not recommended for our local router hash, as we may not be either in the cache or netdb, or may not be publishing an IP.

      As of 0.9.32, works only for literal IPs, returns null for hostnames.

      Parameters:
      peer - not ourselves - use getOurCountry() for that
      Returns:
      two-letter lower-case country code or xx for non-banned peers, or null otherwise
    • getCountry

      public String getCountry(String ip)
      The two-letter country code for an IP address string.
      Parameters:
      ip - IP address string
      Returns:
      two-letter country code or null if unknown
    • getCountryFromIPAddress

      private String getCountryFromIPAddress(String ipAddress)
      Convert IP string to Hash and call existing getCountry method Return two-letter country code or null if unknown.
      Parameters:
      ipAddress - the IP address string to resolve and look up
      Returns:
      the country from IP address
    • cleanupCountryCache

      private void cleanupCountryCache()
      Clean up stale entries from the country cache.
    • getCountryName

      public String getCountryName(String c)
      Full name for a country code, or the code if we don't know the name.
      Parameters:
      c - the two letter country code to resolve
      Returns:
      the untranslated country name, or the code itself when geoIP data is unavailable
    • getCountries

      public Map<String,String> getCountries()
      Provides country code mappings.
      Returns:
      Unmodifiable map of lower-case country codes to untranslated names. Returns empty map if geoIP data is unavailable.
    • getDomain

      public static String getDomain(String hostname)
      Domain name from a reverse DNS hostname.

      Multi-part TLDs (co.uk, com.au, ...) are recognized so the registrable domain is returned rather than a bare second-level label.

      Parameters:
      hostname - the reverse DNS host name to reduce to its registrable domain
      Returns:
      domain name only from reverse dns hostname lookups
    • getIP

      static byte[] getIP(RouterInfo ri)
      Return first IP (v4 or v6) we find, any transport. Not validated, may be local, etc.

      As of 0.9.32, works only for literal IPs, returns null for host names.

      Parameters:
      ri - the RouterInfo whose addresses are scanned for a literal IP
      Returns:
      IP or null
    • getValidIP

      public static byte[] getValidIP(RouterInfo ri)
      Return first valid IP (v4 or v6) we find, any transport. Local and other invalid IPs will not be returned.

      As of 0.9.32, works only for literal IPs, returns null for host names.

      Parameters:
      ri - the RouterInfo whose addresses are scanned for a publicly routable IP
      Returns:
      IP or null
    • getCompatibleIP

      public static byte[] getCompatibleIP(RouterInfo ri)
      IP address compatible with our capabilities (IPv4/IPv6). Prefers IPv4 if we support it.
      Parameters:
      ri - RouterInfo to get IP from
      Returns:
      IP or null
    • getFirstValidIPOfType

      static byte[] getFirstValidIPOfType(RouterInfo ri, boolean wantIPv6)
      First valid IP of the specified type (IPv4 or IPv6).
      Parameters:
      ri - the RouterInfo whose addresses are scanned
      wantIPv6 - true to accept a 16 byte address, false to accept a 4 byte one
      Returns:
      the first valid IP of type
    • supportsIPv4

      static boolean supportsIPv4(RouterContext ctx)
      Check if we support outbound IPv4 connections.
      Parameters:
      ctx - the router context supplying the transport enablement properties
      Returns:
      true when NTCP or UDP is enabled, so an outbound IPv4 connection is possible
    • supportsIPv6

      static boolean supportsIPv6(RouterContext ctx)
      Check if we support outbound IPv6 connections.
      Parameters:
      ctx - the router context supplying the transport enablement properties
      Returns:
      true when neither NTCP nor UDP has IPv6 explicitly disabled
    • evictExpired

      static int evictExpired(Map<Hash,String> cache, Map<Hash,Long> timestamps, long now, long expiryMs)
      Remove stale and unresolved ("xx") entries from the country cache.

      An entry is expired when its timestamp is strictly older than expiryMs; "xx" (unresolved) entries are always dropped. Called both periodically from getCountry(Hash) and as the first stage of trimToCapacity(Map, Map, long, long, int) so the hot path never recomputes the loop.

      Parameters:
      cache - country cache to drain (entries removed in place)
      timestamps - parallel timestamp map for the same keys
      now - cache age reference in millis
      expiryMs - age threshold in millis (strict >)
      Returns:
      number of entries removed
    • trimToCapacity

      static void trimToCapacity(Map<Hash,String> cache, Map<Hash,Long> timestamps, long now, long expiryMs, int maxSize)
      Bring an over-capacity country cache back under its size bound.

      First advances the expired-entry pass via evictExpired(Map, Map, long, long); if still at or above maxSize, drops the oldest quarter by timestamp. The redundant removeIf()/stream cascade in the original facade is preserved, so hot-path behavior (evict expired first, then oldest) is unchanged.

      Parameters:
      cache - country cache to shrink (entries removed in place)
      timestamps - parallel timestamp map for the same keys
      now - cache age reference in millis
      expiryMs - age threshold in millis for the expired-entry pass
      maxSize - size bound that triggers trimming
    • isUnknownCountryCode

      static boolean isUnknownCountryCode(String code)
      True when the cache stores an unresolved-country marker ("xx").
      Parameters:
      code - the cached country name to test
      Returns:
      true for the unresolved "xx" marker, and for a null code