class GeoIP

Constants

CountryCode

Ordered list of the ISO3166 2-character country codes, ordered by GeoIP ID

CountryCode3

Ordered list of the ISO3166 3-character country codes, ordered by GeoIP ID

CountryContinent

Ordered list of the ISO3166 2-character continent code of the countries, ordered by GeoIP ID

CountryName

Ordered list of the English names of the countries, ordered by GeoIP ID

DATA_DIR

The data/ directory for geoip

GEOIP_ASNUM_EDITION
GEOIP_CITY_EDITION_REV0
GEOIP_CITY_EDITION_REV1
GEOIP_CITY_EDITION_REV1_V6
GEOIP_COUNTRY_EDITION
GEOIP_COUNTRY_EDITION_V6
GEOIP_ISP_EDITION
GEOIP_NETSPEED_EDITION
GEOIP_ORG_EDITION
GEOIP_PROXY_EDITION
GEOIP_REGION_EDITION_REV0
GEOIP_REGION_EDITION_REV1
RegionName

Load a hash of region names by region code

TimeZone

Hash of the timezone codes mapped to timezone name, per zoneinfo

VERSION

The GeoIP GEM version number

Attributes

databaseType[R]

The Edition number that identifies which kind of database you've opened

database_type[R]

The Edition number that identifies which kind of database you've opened

local_ip_alias[RW]

An IP that is used instead of local IPs

Public Class Methods

new(filename, flags = 0) click to toggle source

Open the GeoIP database and determine the file format version.

filename is a String holding the path to the GeoIP.dat file options is an integer holding caching flags (unimplemented)

# File lib/geoip.rb, line 176
def initialize(filename, flags = 0)
  @mutex = unless IO.respond_to?(:pread)
             Mutex.new
           end

  @flags = flags
  @database_type = GEOIP_COUNTRY_EDITION
  @record_length = STANDARD_RECORD_LENGTH
  @file = File.open(filename, 'rb')

  detect_database_type!
end

Public Instance Methods

asn(hostname) click to toggle source

Search a ASN GeoIP database for the specified host, returning the AS number and description.

hostname is a String holding the host's DNS name or numeric IP address.

Returns the AS number and description.

Source: geolite.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz

# File lib/geoip.rb, line 370
def asn(hostname)
  ip = lookup_ip(hostname)

  # Convert numeric IP address to an integer
  ipnum = iptonum(ip)

  if (@database_type != GEOIP_ASNUM_EDITION)
    throw "Invalid GeoIP database type, can't look up ASN by IP"
  end

  pos = seek_record(ipnum)
  off = pos + (2*@record_length - 1) * @database_segments[0]

  record = atomic_read(MAX_ASN_RECORD_LENGTH, off)
  record = record.sub(/\000.*/, '')

  # AS####, Description
  ASN.new($1, $2) if record =~ /^(AS\d+)\s(.*)$/
end
city(hostname) click to toggle source

Search the GeoIP database for the specified host, returning city info.

hostname is a String holding the host's DNS name or numeric IP address.

Returns a City object with the fourteen elements:

  • The host or IP address string as requested

  • The IP address string after looking up the host

  • The two-character country code (ISO 3166-1 alpha-2)

  • The three-character country code (ISO 3166-2 alpha-3)

  • The ISO 3166 English-language name of the country

  • The two-character continent code

  • The region name (state or territory)

  • The city name

  • The postal code (zipcode)

  • The latitude

  • The longitude

  • The USA dma_code if known (only REV1 City database)

  • The USA area_code if known (only REV1 City database)

  • The timezone name, if known

# File lib/geoip.rb, line 302
def city(hostname)
  ip = lookup_ip(hostname)

  if (@database_type == GEOIP_CITY_EDITION_REV0 ||
      @database_type == GEOIP_CITY_EDITION_REV1)
    # Convert numeric IP address to an integer
    ipnum = iptonum(ip)
    pos = seek_record(ipnum)
  elsif (@database_type == GEOIP_CITY_EDITION_REV1_V6)
    ipaddr = IPAddr.new ip
    pos = seek_record_v6(ipaddr.to_i)
  else
    throw "Invalid GeoIP database type, can't look up City by IP"
  end

  # This next statement was added to MaxMind's C version after it was
  # rewritten in Ruby. It prevents unassigned IP addresses from returning
  # bogus data.  There was concern over whether the changes to an
  # application's behaviour were always correct, but this has been tested
  # using an exhaustive search of the top 16 bits of the IP address space.
  # The records where the change takes effect contained *no* valid data. 
  # If you're concerned, email me, and I'll send you the test program so
  # you can test whatever IP range you think is causing problems,
  # as I don't care to undertake an exhaustive search of the 32-bit space.
  unless pos == @database_segments[0]
    read_city(pos, hostname, ip)
  end
end
country(hostname) click to toggle source

Search the GeoIP database for the specified host, returning country info.

hostname is a String holding the host's DNS name or numeric IP address.

If the database is a City database (normal), return the result that city would return.

Otherwise, return a Country object with the seven elements:

  • The host or IP address string as requested

  • The IP address string after looking up the host

  • The GeoIP country-ID as an integer (N.B. this is excluded from the city results!)

  • The two-character country code (ISO 3166-1 alpha-2)

  • The three-character country code (ISO 3166-2 alpha-3)

  • The ISO 3166 English-language name of the country

  • The two-character continent code

# File lib/geoip.rb, line 208
def country(hostname)
  if (@database_type == GEOIP_CITY_EDITION_REV0 ||
      @database_type == GEOIP_CITY_EDITION_REV1 ||
      @database_type == GEOIP_CITY_EDITION_REV1_V6)
    return city(hostname)
  end

  if (@database_type == GEOIP_REGION_EDITION_REV0 ||
      @database_type == GEOIP_REGION_EDITION_REV1)
    return region(hostname)
  end

  ip = lookup_ip(hostname)
  if (@database_type == GEOIP_COUNTRY_EDITION ||
      @database_type == GEOIP_PROXY_EDITION ||
      @database_type == GEOIP_NETSPEED_EDITION)
      # Convert numeric IP address to an integer
      ipnum = iptonum(ip)
      code = (seek_record(ipnum) - COUNTRY_BEGIN)
  elsif @database_type == GEOIP_COUNTRY_EDITION_V6
    ipaddr = IPAddr.new ip
    code = (seek_record_v6(ipaddr.to_i) - COUNTRY_BEGIN)
  else
    throw "Invalid GeoIP database type, can't look up Country by IP"
  end

  Country.new(
    hostname,                   # Requested hostname
    ip,                         # Ip address as dotted quad
    code,                       # GeoIP's country code
    CountryCode[code],          # ISO3166-1 alpha-2 code
    CountryCode3[code],         # ISO3166-2 alpha-3 code
    CountryName[code],          # Country name, per ISO 3166
    CountryContinent[code]      # Continent code.
  )
end
each() { |rec| ... } click to toggle source

Iterate through a GeoIP city database

# File lib/geoip.rb, line 401
def each
  return enum_for unless block_given?

  if (@database_type != GEOIP_CITY_EDITION_REV0 &&
      @database_type != GEOIP_CITY_EDITION_REV1)
    throw "Invalid GeoIP database type, can't iterate thru non-City database"
  end

  @iter_pos = @database_segments[0] + 1
  num = 0

  until ((rec = read_city(@iter_pos)).nil?)
    yield rec
    print "#{num}: #{@iter_pos}\n" if((num += 1) % 1000 == 0)
  end

  @iter_pos = nil
  return self
end
isp(hostname) click to toggle source

Search a ISP GeoIP database for the specified host, returning the ISP Not all GeoIP databases contain ISP information. Check maxmind.com

hostname is a String holding the host's DNS name or numeric IP address.

Returns the ISP name.

# File lib/geoip.rb, line 340
def isp(hostname)
  ip = lookup_ip(hostname)

  # Convert numeric IP address to an integer
  ipnum = iptonum(ip)

  if (@database_type != GEOIP_ISP_EDITION &&
      @database_type != GEOIP_ORG_EDITION)
    throw "Invalid GeoIP database type, can't look up Organization/ISP by IP"
  end

  pos = seek_record(ipnum)
  off = pos + (2*@record_length - 1) * @database_segments[0]

  record = atomic_read(MAX_ORG_RECORD_LENGTH, off)
  record = record.sub(/\000.*/, '')
  record.start_with?('*') ? nil : ISP.new(record)
end
Also aliased as: organization
organization(hostname)

Search a ISP GeoIP database for the specified host, returning the organization.

hostname is a String holding the host's DNS name or numeric IP address.

Returns the organization associated with it.

Alias for: isp
region(hostname) click to toggle source

Search the GeoIP database for the specified host, retuning region info.

hostname is a String holding the hosts's DNS name or numeric IP address.

Returns a Region object with the nine elements:

  • The host or IP address string as requested

  • The IP address string after looking up the host

  • The two-character country code (ISO 3166-1 alpha-2)

  • The three-character country code (ISO 3166-2 alpha-3)

  • The ISO 3166 English-language name of the country

  • The two-character continent code

  • The region name (state or territory)

  • The timezone name, if known

# File lib/geoip.rb, line 260
def region(hostname)
  if (@database_type == GEOIP_CITY_EDITION_REV0 ||
      @database_type == GEOIP_CITY_EDITION_REV1 ||
      @database_type == GEOIP_CITY_EDITION_REV1_V6)
    return city(hostname)
  end

  if (@database_type == GEOIP_REGION_EDITION_REV0 ||
      @database_type == GEOIP_REGION_EDITION_REV1)
    ip = lookup_ip(hostname)
    ipnum = iptonum(ip)
    pos = seek_record(ipnum)
  else
    throw "Invalid GeoIP database type, can't look up Region by IP"
  end

  unless pos == @database_segments[0]
    read_region(pos, hostname, ip)
  end
end

Private Instance Methods

lookup_region_name(country_iso2, region_code) click to toggle source
# File lib/geoip.rb, line 515
def lookup_region_name(country_iso2, region_code)
  country_regions = RegionName[country_iso2]
  country_regions && country_regions[region_code]
end
seek_record_v6(ipnum) click to toggle source
# File lib/geoip.rb, line 670
def seek_record_v6(ipnum)

  # Binary search in the file.
  # Records are pairs of little-endian integers, each of @record_length.
  offset = 0
  mask = 1 << 127

  127.downto(0) do |depth|
    off = (@record_length * 2 * offset)
    buf = atomic_read(@record_length * 2, off)

    buf.slice!(0...@record_length) if ((ipnum & mask) != 0)
    offset = le_to_ui(buf[0...@record_length].unpack("C*"))

    if (offset >= @database_segments[0])
      return offset
    end

    mask >>= 1
  end

end