Class: MaxMind::DB

Inherits:
Object
  • Object
show all
Defined in:
lib/maxmind/db.rb,
lib/maxmind/db/errors.rb,
lib/maxmind/db/decoder.rb,
lib/maxmind/db/metadata.rb,
lib/maxmind/db/file_reader.rb,
lib/maxmind/db/memory_reader.rb

Overview

DB provides a way to read MaxMind DB files.

MaxMind DB is a binary file format that stores data indexed by IP address subnets (IPv4 or IPv6).

This class is a pure Ruby implementation of a reader for the format.

Example

require 'maxmind/db'

reader = MaxMind::DB.new('GeoIP2-City.mmdb', mode: MaxMind::DB::MODE_MEMORY)

record = reader.get('1.1.1.1')
if record.nil?
puts '1.1.1.1 was not found in the database'
else
puts record['country']['iso_code']
puts record['country']['names']['en']
end

reader.close

Defined Under Namespace

Classes: InvalidDatabaseError, Metadata

Constant Summary collapse

MODE_AUTO =

Choose the default method to open the database. Currently the default is MODE_FILE.

:MODE_AUTO
MODE_FILE =

Open the database as a regular file and read on demand.

:MODE_FILE
MODE_MEMORY =

Read the database into memory. This is faster than MODE_FILE but causes increased memory use.

:MODE_MEMORY

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(database, options = {}) ⇒ DB

Create a DB. A DB provides a way to read MaxMind DB files. If you're performing multiple lookups, it's most efficient to create one DB and reuse it.

Once created, the DB is safe to use for lookups from multiple threads. It is safe to use after forking only if you use MODE_MEMORY or if your version of Ruby supports IO#pread.

Parameters:

  • database (String)

    a path to a MaxMind DB.

  • options (Hash<Symbol, Object>) (defaults to: {})

    options controlling the behavior of the DB.

Options Hash (options):

  • :mode (Symbol)

    Defines how to open the database. It may be one of MODE_AUTO, MODE_FILE, or MODE_MEMORY. If you don't provide one, DB uses MODE_AUTO. Refer to the definition of those constants for an explanation of their meaning.

  • :max_values (Integer)

    The maximum number of values a single record, or the metadata, may decode to. The default is 65,536. The largest records MaxMind produces decode to a few hundred values.

  • :max_payload_bytes (Integer)

    The maximum total size in bytes of the strings, bytes, and integers a single record, or the metadata, may decode. The default is 2 MiB. The largest records MaxMind produces hold about a kilobyte.

  • :max_depth (Integer)

    The maximum nesting depth of maps, arrays, and pointers in a single record, or the metadata. The default is 512.

Raises:

  • (InvalidDatabaseError)

    if the database is corrupt or invalid. A database that exceeds any of the limits above raises this error from the lookup, or from this constructor if the metadata exceeds them.

  • (ArgumentError)

    if the mode or a limit is invalid.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/maxmind/db.rb', line 101

def initialize(database, options = {})
  options[:mode] = MODE_AUTO unless options.key?(:mode)
  limits = decoder_limits(options)

  case options[:mode]
  when MODE_AUTO, MODE_FILE
    @io = FileReader.new(database)
  when MODE_MEMORY
    @io = MemoryReader.new(database)
  when MODE_PARAM_IS_BUFFER
    @io = MemoryReader.new(database, is_buffer: true)
  else
    raise ArgumentError, 'Invalid mode'
  end

  begin
    @size = @io.size

     = 
     = Decoder.new(@io, , **limits)
    , = .decode()
     = .new()
    @decoder = Decoder.new(@io, .search_tree_size +
                           DATA_SECTION_SEPARATOR_SIZE, **limits)

    # Store copies as instance variables to reduce method calls.
    @ip_version       = .ip_version
    @node_count       = .node_count
    @node_byte_size   = .node_byte_size
    @record_size      = .record_size
    @search_tree_size = .search_tree_size

    @ipv4_start = nil
    # Find @ipv4_start up front. If we don't, we either have a race to
    # get/set it or have to synchronize access.
    start_node(0)
  rescue StandardError => e
    @io.close
    raise e
  end
end

Instance Attribute Details

#metadataMaxMind::DB::Metadata (readonly)

Return the metadata associated with the MaxMind DB



62
63
64
# File 'lib/maxmind/db.rb', line 62

def 
  
end

Instance Method Details

#closevoid

This method returns an undefined value.

Close the DB and return resources to the system.



332
333
334
# File 'lib/maxmind/db.rb', line 332

def close
  @io.close
end

#get(ip_address) ⇒ Object?

Return the record for the IP address in the MaxMind DB. The record can be one of several types and depends on the contents of the database.

If no record is found for the IP address, get returns nil.

Parameters:

  • ip_address (String, IPAddr)

    IPv4 or IPv6 address.

Returns:

  • (Object, nil)

Raises:

  • (ArgumentError)

    if you attempt to look up an IPv6 address in an IPv4-only database.

  • (InvalidDatabaseError)

    if the database is corrupt or invalid.



157
158
159
160
161
# File 'lib/maxmind/db.rb', line 157

def get(ip_address)
  record, = get_with_prefix_length(ip_address)

  record
end

#get_with_prefix_length(ip_address) ⇒ Array<(Object, Integer)>

Return an array containing the record for the IP address in the MaxMind DB and its associated network prefix length. The record can be one of several types and depends on the contents of the database.

If no record is found for the IP address, the record will be nil and the prefix length will be the value for the missing network.

Parameters:

  • ip_address (String, IPAddr)

    IPv4 or IPv6 address.

Returns:

  • (Array<(Object, Integer)>)

Raises:

  • (ArgumentError)

    if you attempt to look up an IPv6 address in an IPv4-only database.

  • (InvalidDatabaseError)

    if the database is corrupt or invalid.



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/maxmind/db.rb', line 179

def get_with_prefix_length(ip_address)
  ip = ip_address.is_a?(IPAddr) ? ip_address : IPAddr.new(ip_address)

  # We could check the IP has the correct prefix (32 or 128) but I do not
  # for performance reasons.

  ip_version = ip.ipv6? ? 6 : 4
  if ip_version == 6 && @ip_version == 4
    raise ArgumentError,
          "Error looking up #{ip}. You attempted to look up an IPv6 address in an IPv4-only database."
  end

  pointer, depth = find_address_in_tree(ip, ip_version)
  return nil, depth if pointer == 0

  [resolve_data_pointer(pointer), depth]
end