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_CABLEDSL_SPEED
GEOIP_CITY_EDITION_REV0
GEOIP_CITY_EDITION_REV1
GEOIP_CITY_EDITION_REV1_V6
GEOIP_CORPORATE_SPEED
GEOIP_COUNTRY_EDITION
GEOIP_COUNTRY_EDITION_V6
GEOIP_DIALUP_SPEED
GEOIP_ISP_EDITION
GEOIP_NETSPEED_EDITION
GEOIP_NETSPEED_EDITION_REV1
GEOIP_ORG_EDITION
GEOIP_PROXY_EDITION
GEOIP_REGION_EDITION_REV0
GEOIP_REGION_EDITION_REV1
GEOIP_UNKNOWN_SPEED

Numeric codes for NETSPEED (NETSPEED_REV1* is string-based):

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, options = {}) 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 a Hash allowing you to specify the caching options

# File lib/geoip.rb, line 225
def initialize(filename, options = {})
  if options[:preload] || !IO.respond_to?(:pread)
    @mutex = Mutex.new
  end

  @use_pread = IO.respond_to?(:pread) && !options[:preload]
  @contents = nil
  @iter_pos = nil
  @options = options
  @database_type = Edition::COUNTRY
  @record_length = STANDARD_RECORD_LENGTH
  @file = File.open(filename, 'rb')

  detect_database_type!

  preload_data if options[:preload]
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.

Many other types of GeoIP database (e.g. userType) mis-identify as ASN type, and this can read those too.

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 431
def asn(hostname)
  ip = lookup_ip(hostname)

  if (@database_type == Edition::ASNUM)
    # Convert numeric IP address to an integer
    ipnum = iptonum(ip)
    pos = seek_record(ipnum)
  elsif (@database_type == Edition::ASNUM_V6)
    ipaddr = IPAddr.new ip
    pos = seek_record(ipaddr.to_i)
  else
    throw "Invalid GeoIP database type #{@database_type}, can't look up ASN by IP"
  end

  read_asn(pos-@database_segments[0])
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 364
def city(hostname)
  ip = lookup_ip(hostname)

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

  read_city(pos-@database_segments[0], hostname, ip)
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 262
def country(hostname)
  case @database_type
  when Edition::CITY_REV0, Edition::CITY_REV1, Edition::CITY_REV1_V6
    city(hostname)

  when Edition::REGION_REV0, Edition::REGION_REV1
    region(hostname)

  when Edition::NETSPEED, Edition::NETSPEED_REV1
    netspeed(hostname)

  when Edition::COUNTRY, Edition::PROXY, Edition::COUNTRY_V6
    ip = lookup_ip(hostname)
    if @ip_bits > 32
      ipaddr = IPAddr.new ip
      code = (seek_record(ipaddr.to_i) - COUNTRY_BEGIN)
    else
      # Convert numeric IP address to an integer
      ipnum = iptonum(ip)
      code = (seek_record(ipnum) - @database_segments[0])
    end
    read_country(code, hostname, ip)
  else
    throw "Invalid GeoIP database type #{@database_type}, can't look up Country by IP"
  end
end
each() { |rec| ... } click to toggle source

Iterate through a GeoIP city database by

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

  if (@database_type != Edition::CITY_REV0 &&
      @database_type != Edition::CITY_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
each_by_ip(offset = 0, ipnum = 0, mask = nil) { |ipnum, val > 0 ? read_record(ipnum, ipnum, val) : nil| ... } click to toggle source

Call like this, for example: GeoIP.new(‘GeoIPNetSpeedCell.dat’).each{|*a| puts(“0x%08Xt%d” % a)} or: GeoIP.new(‘GeoIPv6.dat’).each{|*a| puts(“0x%032Xt%d” % a)}

# File lib/geoip.rb, line 483
def each_by_ip offset = 0, ipnum = 0, mask = nil, &callback
  mask ||= 1 << (@ip_bits-1)

  # Read the two pointers and split them:
  record2 = atomic_read(@record_length*2, @record_length*2*offset)
  record1 = record2.slice!(0, @record_length)

  # Traverse the left tree
  off1 = le_to_ui(record1.unpack('C*'))
  val = off1 - @database_segments[0]
  if val >= 0
    yield(ipnum, val > 0 ? read_record(ipnum.to_s, ipnum, val) : nil) 
  elsif mask != 0
    each_by_ip(off1, ipnum, mask >> 1, &callback)
  end

  # Traverse the right tree
  off2 = le_to_ui(record2.unpack('C*'))
  val = off2 - @database_segments[0]
  if val >= 0
    yield(ipnum|mask, val > 0 ? read_record(ipnum.to_s, ipnum, val) : nil)
  elsif mask != 0
    each_by_ip(off2, ipnum|mask, mask >> 1, &callback)
  end
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 391
def isp(hostname)
  ip = lookup_ip(hostname)

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

  case @database_type
  when Edition::ORG,
   Edition::ISP,
   Edition::DOMAIN,
   Edition::ASNUM,
   Edition::ACCURACYRADIUS,
   Edition::NETSPEED,
   Edition::USERTYPE,
   Edition::REGISTRAR,
   Edition::LOCATIONA,
   Edition::CITYCONF,
   Edition::COUNTRYCONF,
   Edition::REGIONCONF,
   Edition::POSTALCONF
    pos = seek_record(ipnum)
    read_isp(pos-@database_segments[0])
  else
    throw "Invalid GeoIP database type, can't look up Organization/ISP by IP"
  end
end
Also aliased as: organization
netspeed(hostname) click to toggle source

Search a GeoIP Connection Type (Netspeed) database for the specified host, returning the speed code.

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

# File lib/geoip.rb, line 293
def netspeed(hostname)
  unless (@database_type == Edition::NETSPEED ||
      @database_type == Edition::NETSPEED_REV1)
    throw "Invalid GeoIP database type #{@database_type}, can't look up Netspeed by IP"
  end
  # Convert numeric IP address to an integer
  ip = lookup_ip(hostname)
  ipnum = iptonum(ip)
  pos = seek_record(ipnum)
  read_netspeed(pos-@database_segments[0])
end
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, returning 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 320
def region(hostname)
  if (@database_type == Edition::CITY_REV0 ||
      @database_type == Edition::CITY_REV1 ||
      @database_type == Edition::CITY_REV1_V6)
    return city(hostname)
  end

  if (@database_type == Edition::REGION_REV0 ||
      @database_type == Edition::REGION_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

  if pos == @database_segments[0]
    nil
  else
    read_region(pos, hostname, ip)
  end
end

Private Instance Methods

atomic_read_unguarded(length, pos) click to toggle source
# File lib/geoip.rb, line 926
def atomic_read_unguarded(length, pos)
  if @use_pread
    IO.pread(@file.fileno, length, pos)
  else
    io = @contents || @file
    io.seek(pos)
    io.read(length)
  end
end
index_size() click to toggle source

Size of the database index (a binary tree of depth <= @ip_bits)

# File lib/geoip.rb, line 742
def index_size
  2 * @record_length * @database_segments[0]
end
lookup_region_name(country_iso2, region_code) click to toggle source
# File lib/geoip.rb, line 746
def lookup_region_name(country_iso2, region_code)
  country_regions = RegionName[country_iso2]
  country_regions && country_regions[region_code]
end
preload_data() click to toggle source

Loads data into a StringIO which is Copy-on-write friendly

# File lib/geoip.rb, line 539
def preload_data
  @file.seek(0)
  @contents = StringIO.new(@file.read)
  @file.close
end
read_asn(offset) click to toggle source
# File lib/geoip.rb, line 710
def read_asn offset
  return nil if offset == 0
  record = atomic_read(MAX_ASN_RECORD_LENGTH, index_size+offset)
  record.slice!(record.index("\0")..-1)

  # AS####, Description
  if record =~ /^(AS\d+)(?:\s(.*))?$/
    # set the correct encoding in ruby 1.9 compatible environments:
    asn = $2.respond_to?(:force_encoding) ? $2.force_encoding('iso-8859-1').encode('utf-8') : $2
    ASN.new($1, asn)
  else
    record
  end
end
read_country(code, hostname, ip) click to toggle source
# File lib/geoip.rb, line 658
def read_country code, hostname, ip
  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
read_isp(offset) click to toggle source
# File lib/geoip.rb, line 734
def read_isp offset
  record = atomic_read(MAX_ORG_RECORD_LENGTH, index_size+offset)
  record = record.sub(/\000.*/n, '')
  record = record.force_encoding('iso-8859-1').encode('utf-8') if record.respond_to?(:force_encoding)
  record.start_with?('*') ? nil : ISP.new(record)
end
read_netspeed(offset) click to toggle source
# File lib/geoip.rb, line 725
def read_netspeed(offset)
  return offset if @database_type == Edition::NETSPEED  # Numeric value
  return nil if offset == 0

  record = atomic_read(20, index_size+offset)
  record.slice!(record.index("\0")..-1)
  record
end
read_record(hostname, ip, offset) click to toggle source
# File lib/geoip.rb, line 511
def read_record hostname, ip, offset
  case @database_type
  when Edition::CITY_REV0, Edition::CITY_REV1, Edition::CITY_REV1_V6
    read_city(offset, hostname, ip)

  when Edition::REGION_REV0, Edition::REGION_REV1
    read_region(offset+@database_segments[0], hostname, ip)

  when Edition::NETSPEED, Edition::NETSPEED_REV1
    read_netspeed(offset)

  when Edition::COUNTRY, Edition::PROXY, Edition::COUNTRY_V6
    read_country(offset, hostname, ip)

  when Edition::ASNUM, Edition::ASNUM_V6
    read_asn(offset)

  # Add new types here
  when Edition::ISP, Edition::ORG
    read_isp offset

  else
    #raise "Unsupported GeoIP database type #{@database_type}"
    offset
  end
end