Skip to content

Instantly share code, notes, and snippets.

@edvakf
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edvakf/b64cf467c4733b7f7b73 to your computer and use it in GitHub Desktop.
Save edvakf/b64cf467c4733b7f7b73 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# coding: utf-8
require 'set'
module GeoLite2_Country
class IPv4Block < Struct.new(:start_ip,
:mask_length,
:geoname_id)
def self.from_line(line)
c = line.strip.split(",")
network_start_ip = c[0]
network_mask_length = c[1].to_i
geoname_id = c[2].to_i
if network_start_ip =~ /^::ffff:(\d+)\.(\d+)\.(\d+)\.(\d+)$/
start_ip = ($1.to_i << 24) | ($2.to_i << 16) | ($3.to_i << 8) | $4.to_i
mask_length = network_mask_length - 96
else
raise 'Bad IP'
end
self.new(start_ip, mask_length, geoname_id)
end
end
class IPv4BlockLister
def initialize(mask, in_fp = $stdin, out_fp = $stdout)
@keep = []
@in = in_fp
@out = out_fp
@mask = mask
end
def run
header = @in.gets
while line = @in.gets
next if line[0..6] != '::ffff:'
cur = IPv4Block.from_line(line)
if !@keep.empty? && @keep.last.start_ip >> (32 - @mask) != cur.start_ip >> (32 - @mask)
flush
end
if cur.mask_length == @mask
@out.puts [cur.start_ip, cur.geoname_id].join("\t")
elsif cur.mask_length < @mask
(0...(1 << (@mask - cur.mask_length))).each do |n|
@out.puts [cur.start_ip | (n << (32 - @mask)), cur.geoname_id].join("\t")
end
else
@keep.push(cur)
end
end
if !@keep.empty?
flush
end
end
private
def flush
geoname_ids = @keep.inject(Set.new){|ids, b| ids << b.geoname_id}
geoname_ids.delete(0)
if geoname_ids.length == 1
@out.puts [(@keep.last.start_ip >> (32 - @mask)) << (32 - @mask), geoname_ids.first].join("\t")
else
# ambiguous
@out.puts [(@keep.last.start_ip >> (32 - @mask)) << (32 - @mask), 0].join("\t")
end
@keep = []
end
end
end
GeoLite2_Country::IPv4BlockLister.new(24).run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment