Module: Hvor::IpNormalizer

Defined in:
app/services/hvor/ip_normalizer.rb

Defined Under Namespace

Classes: Result

Constant Summary collapse

IPV6_HEX_WIDTH =
32

Class Method Summary collapse

Class Method Details

.normalize(ip) ⇒ Object

Returns a Result with:

version: :ipv4 or :ipv6
value:   Integer (0..2**32-1) for ipv4, zero-padded 32-char lowercase
       hex String for ipv6 -- the same representation IPRange uses
       for its ipv4_from/to and ipv6_from/to columns, so callers can
       compare/query directly against it.

Raises:



19
20
21
22
23
24
25
26
27
28
29
30
# File 'app/services/hvor/ip_normalizer.rb', line 19

def normalize(ip)
  raise InvalidIpError, "ip is blank" if ip.nil? || ip.to_s.strip.empty?

  address = parse(strip_zone_id(ip.to_s.strip))
  address = address.native if address.ipv4_mapped?

  if address.ipv4?
    Result.new(:ipv4, address.to_i)
  else
    Result.new(:ipv6, address.to_i.to_s(16).rjust(IPV6_HEX_WIDTH, "0"))
  end
end

.parse(ip) ⇒ Object



32
33
34
35
36
# File 'app/services/hvor/ip_normalizer.rb', line 32

def parse(ip)
  IPAddr.new(ip)
rescue IPAddr::Error => e
  raise InvalidIpError, "#{ip.inspect} is not a valid IP address (#{e.message})"
end

.strip_zone_id(ip) ⇒ Object

A zone ID (e.g. "fe80::1%eth0") is only meaningful for scoping a link-local address to a network interface -- IPAddr#to_i folds it into the integer value in a way that no longer represents the plain 128-bit address, so it must be dropped before converting to our lookup representation.



42
43
44
# File 'app/services/hvor/ip_normalizer.rb', line 42

def strip_zone_id(ip)
  ip.sub(/%.*\z/, "")
end