Long time no blog! I’ve had this script sitting around for a while now and never really quite know where to post it.
It isn’t really worth turning into a Gem and while I just posted it on Github, posting it here will probably give people a few more google results. Since it’s the internet, I’m sure there are some edge cases where this doesn’t work or where it returns incorrect data. It’s also very fragile when it comes to error handling with the downloads, so don’t use it to power your nuclear power plant ;)

1
2
3
4
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
require 'open-uri'
require 'ipaddr'

def generate_lookup_table
  delegation_lists = [
  'ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest',
  'ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-latest',
  'ftp://ftp.arin.net/pub/stats/arin/delegated-arin-latest',
  'ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest',
  'ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-latest'
  ]
  lookup_table = {}
  delegation_lists.each do |list|
    puts "List: #{list}"
    open(list).read.each_line do |line|
      next unless line.include?('ipv4')
      # http://www.apnic.net/publications/media-library/documents/resource-guidelines/rir-statistics-exchange-format
      registry, cc, type, start, value, date, status, extensions = line.split('|')
      ipaddr1 = IPAddr.new start rescue next
      ipaddr2 = IPAddr.new(ipaddr1.to_i + value.to_i, Socket::AF_INET)
      range = ipaddr1.to_i..(ipaddr1.to_i + value.to_i)
      lookup_table[cc] ||= []
      lookup_table[cc] << range
      puts "#{ipaddr1} - #{ipaddr2}: #{cc}"
    end
  end
end

persisted_table_filename = 'ip_countrycode_lookup.dat'

if File.exists?(persisted_table_filename)
  lookup_table = File.open(persisted_table_filename) { |file| Marshal.load(file) }
  puts "Loaded persisted table."
else
  lookup_table = generate_lookup_table
  File.open(persisted_table_filename,'w') { |file| Marshal.dump(lookup_table, file) }
  puts "Created new lookup table and persisted it."
end

loop do
  puts "Enter your IP:"
  ip_input = IPAddr.new(gets.chomp).to_i rescue next
  result = 'n/a'
  lookup_table.each_pair do |cc, ranges|
    if not ranges.select{|range| range.include?(ip_input)}.empty?
      result = cc
      break
    end
  end
  puts result
end

Comments