Module: OpenASN::IP

Defined in:
lib/openasn/ip.rb

Overview

IP input parsing. Hot path: dotted-quad IPv4 strings (the overwhelming majority of real lookups) are parsed by hand — ~6x faster than IPAddr.new, which matters when the whole lookup budget is ~20µs. Everything else (IPv6, IPAddr instances, mapped addresses) goes through IPAddr for correctness.

Class Method Summary collapse

Class Method Details

.fast_v4(str) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/openasn/ip.rb', line 27

def fast_v4(str)
  parts = str.split(".")
  return nil unless parts.length == 4

  int = 0
  parts.each do |p|
    # Reject empty octets, leading zeros ("01" is ambiguous octal in
    # many parsers — safer to fall through to IPAddr, which rejects it),
    # non-digits, and >255.
    return nil if p.empty? || p.length > 3 || (p.length > 1 && p.start_with?("0"))

    n = 0
    p.each_char do |c|
      d = c.ord - 48
      return nil if d.negative? || d > 9

      n = n * 10 + d
    end
    return nil if n > 255

    int = (int << 8) | n
  end
  [:ipv4, int]
end

.from_ipaddr(ip) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/openasn/ip.rb', line 58

def from_ipaddr(ip)
  if ip.ipv4?
    [:ipv4, ip.to_i]
  elsif ip.ipv4_mapped?
    # ::ffff:1.2.3.4 arrives on dual-stack sockets constantly; classify
    # as the embedded IPv4 — that's the address doing the talking.
    [:ipv4, ip.native.to_i]
  else
    [:ipv6, ip.to_i]
  end
end

.from_string(str) ⇒ Object



52
53
54
55
56
# File 'lib/openasn/ip.rb', line 52

def from_string(str)
  from_ipaddr(IPAddr.new(str))
rescue IPAddr::Error
  raise InvalidIPError, "invalid IP address: #{str.inspect}"
end

.parse(input) ⇒ Object

-> [family(:ipv4 | :ipv6), Integer] Raises OpenASN::InvalidIPError (an ArgumentError) on anything else.



16
17
18
19
20
21
22
23
24
25
# File 'lib/openasn/ip.rb', line 16

def parse(input)
  case input
  when IPAddr
    from_ipaddr(input)
  when String
    fast_v4(input) || from_string(input)
  else
    raise InvalidIPError, "expected an IP address String or IPAddr, got #{input.class}"
  end
end