Class: IpToAsn::Lookup

Inherits:
Object
  • Object
show all
Defined in:
lib/iptoasn/iptoasn.rb

Instance Method Summary collapse

Constructor Details

#initialize(db_ip2asn: nil, db_country: nil, db_names: nil) ⇒ Lookup

rubocop:disable Metrics/AbcSize



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/iptoasn/iptoasn.rb', line 5

def initialize(db_ip2asn: nil, db_country: nil, db_names: nil) # rubocop:disable Metrics/AbcSize
  if db_ip2asn.nil? || db_country.nil? || db_names.nil?
    require 'iptoasn/data'
    db_ip2asn = IpToAsn::Data.ip2asn
    db_country = IpToAsn::Data.countries
    db_names = IpToAsn::Data.asnames
  end

  @db_ip2asn = File.open(db_ip2asn, 'r')
  @db_country = File.read(db_country)
  @db_names = File.open(db_names, 'r')

  @reserved_ranges = {
    IPAddr.new('10.0.0.0/8') => 'RFC1918 Private',
    IPAddr.new('172.16.0.0/12') => 'RFC1918 Private',
    IPAddr.new('192.168.0.0/16') => 'RFC1918 Private',
    IPAddr.new('0.0.0.0/8') => 'Current Network',
    IPAddr.new('127.0.0.0/8') => 'Loopback',
    IPAddr.new('169.254.0.0/16') => 'Link Local'
  }

  @entry_size = IpToAsn::AsEntry.size
  @db_ip2asn.seek(0, IO::SEEK_END)
  @db_total_entries = @db_ip2asn.size / @entry_size
  @db_ip2asn.seek(0)
  @mutex = Mutex.new
end

Instance Method Details

#as_name(name_offset, name_length) ⇒ Object



37
38
39
40
# File 'lib/iptoasn/iptoasn.rb', line 37

def as_name(name_offset, name_length)
  @db_names.seek(name_offset, IO::SEEK_SET)
  @db_names.read(name_length)
end

#country(country_index) ⇒ Object



42
43
44
# File 'lib/iptoasn/iptoasn.rb', line 42

def country(country_index)
  @db_country[country_index * 2, 2]
end

#find_range(ranges:, ip_address:) ⇒ Object



87
88
89
90
91
# File 'lib/iptoasn/iptoasn.rb', line 87

def find_range(ranges:, ip_address:)
  ranges.filter_map do |k, v|
    v if k.include? ip_address
  end.first
end

#ip_to_int(ip) ⇒ Object



33
34
35
# File 'lib/iptoasn/iptoasn.rb', line 33

def ip_to_int(ip)
  ip.split('.').map(&:to_i).inject(0) { |acc, num| (acc << 8) | num }
end

#lookup(ip) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/iptoasn/iptoasn.rb', line 46

def lookup(ip)
  reserved_range_name = find_range(
    ranges: @reserved_ranges,
    ip_address: ip
  )

  return { as_number: 0, country_code: 'XX', as_name: reserved_range_name } unless reserved_range_name.nil?

  @mutex.synchronize do
    search_db(ip)
  end
end

#search_db(ip) ⇒ Object

rubocop:disable Metrics/AbcSize



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/iptoasn/iptoasn.rb', line 59

def search_db(ip) # rubocop:disable Metrics/AbcSize
  ip_int = ip_to_int(ip)

  left = 0
  right = @db_total_entries - 1

  while left <= right
    mid = (left + right) / 2
    @db_ip2asn.seek(mid * @entry_size, IO::SEEK_SET)
    data = @db_ip2asn.read(@entry_size)
    break if data.nil? || data.size < @entry_size

    entry = IpToAsn::AsEntry.deserialize(data)

    if entry.range_start.to_i <= ip_int && ip_int <= entry.range_end.to_i
      return {
        as_number: entry.number,
        as_name: as_name(entry.name_offset, entry.name_length),
        country_code: country(entry.country)
      }
    elsif ip_int < entry.range_start.to_i
      right = mid - 1
    else
      left = mid + 1
    end
  end
end