Skip to content

Commit

Permalink
Added regexs for matching CIDR ranges (ex: 1.2.3.4/24) (closes #474).
Browse files Browse the repository at this point in the history
* Added `Network::IPRange::CIDR::IPV4_REGEX`.
* Added `Network::IPRange::CIDR::IPV6_REGEX`.
* Raise an `ArgumentError` if `IPRange::CIDR#initialize` is given an
  invalid CIDR string.
  • Loading branch information
postmodern committed Feb 10, 2024
1 parent c0260d1 commit 6b5fd7d
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 0 deletions.
43 changes: 43 additions & 0 deletions lib/ronin/support/network/ip_range/cidr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,42 @@ class CIDR < IPAddr

include Enumerable

ipv4_octet = /(?:\d{1,2}|1\d{2}|2[1-4]\d|25[0-5])/
ipv4_addr = /#{ipv4_octet}(?:\.#{ipv4_octet}){3}/

# Regular expression that matches IPv4 CIDR ranges.
#
# @api private
#
# @since 1.1.0
IPV4_REGEX = %r{\A#{ipv4_addr}(?:/(?:\d|[12]\d|3[0-2]))?\z}

# Regular expression that matches IPv6 CIDR ranges.
#
# @api private
#
# @since 1.1.0
IPV6_REGEX = %r{\A(?:
(?:[0-9a-fA-F]{1,4}:){6}#{ipv4_addr}|
(?:[0-9a-fA-F]{1,4}:){5}[0-9a-fA-F]{1,4}:#{ipv4_addr}|
(?:[0-9a-fA-F]{1,4}:){5}:[0-9a-fA-F]{1,4}:#{ipv4_addr}|
(?:[0-9a-fA-F]{1,4}:){1,1}(?::[0-9a-fA-F]{1,4}){1,4}:#{ipv4_addr}|
(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,3}:#{ipv4_addr}|
(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,2}:#{ipv4_addr}|
(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,1}:#{ipv4_addr}|
:(?::[0-9a-fA-F]{1,4}){1,5}:#{ipv4_addr}|
(?:(?:[0-9a-fA-F]{1,4}:){1,5}|:):#{ipv4_addr}|
(?:[0-9a-fA-F]{1,4}:){1,1}(?::[0-9a-fA-F]{1,4}){1,6}|
(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|
(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|
(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|
(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|
(?:[0-9a-fA-F]{1,4}:){1,6}(?::[0-9a-fA-F]{1,4}){1,1}|
[0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){7}|
:(?::[0-9a-fA-F]{1,4}){1,7}|
(?:(?:[0-9a-fA-F]{1,4}:){1,7}|:):
)(?:/(?:\d{1,2}|1[0-1]\d+|12[0-8]))?\z}x

# The CIDR IP range string.
#
# @return [String]
Expand All @@ -59,7 +95,14 @@ class CIDR < IPAddr
# The address family for the CIDR range. This is mainly for
# backwards compatibility with `IPAddr#initialize`.
#
# @raise [ArgumentError]
# The CIDR range string was not a valid IPv4 or IPv6 CIDR range.
#
def initialize(string,family=Socket::AF_UNSPEC)
unless (string =~ IPV4_REGEX || string =~ IPV6_REGEX)
raise(ArgumentError,"invalid CIDR range: #{string.inspect}")
end

super(string,family)

@string = string
Expand Down
131 changes: 131 additions & 0 deletions spec/network/ip_range/cidr_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'spec_helper'
require 'matchers/fully_match'
require 'ronin/support/network/ip_range/cidr'

describe Ronin::Support::Network::IPRange::CIDR do
Expand All @@ -8,12 +9,142 @@
expect(described_class).to be < IPAddr
end

describe "IPV4_REGEX" do
subject { described_class::IPV4_REGEX }

it "must match the full string" do
expect(' 1.2.3.4/24 ').to_not match(subject)
end

it "must match non-CIDR IPv4 addresses" do
expect('1.2.3.4').to fully_match(subject)
end

it "must match '0.0.0.0'" do
expect('0.0.0.0').to fully_match(subject)
end

it "must match '255.255.255.255'" do
expect('255.255.255.255').to fully_match(subject)
end

it "must not match octets greater than 255" do
expect('256.255.255.255').to_not match(subject)
expect('255.256.255.255').to_not match(subject)
expect('255.255.256.255').to_not match(subject)
expect('255.255.256.256').to_not match(subject)
end

it "must match CIDR IPv4 addresses" do
expect('1.2.3.4/24').to fully_match(subject)
end

it "must not match bit lengths greater than 32" do
expect('1.2.3.4/33').to_not match(subject)
end
end

describe "IPV6_REGEX" do
subject { described_class::IPV6_REGEX }

it "must match the full string" do
expect(' 1111:2222:3333:4444:5555:6666:7777:8888/128 ').to_not match(subject)
end

it "must match non-CIDR IPv6 addresses" do
expect('1111:2222:3333:4444:5555:6666:7777:8888').to fully_match(subject)
end

it "must match '0:0:0:0:0:0:0:0'" do
expect('0:0:0:0:0:0:0:0').to fully_match(subject)
end

it "must match 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'" do
expect('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff').to fully_match(subject)
end

it "must match 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF'" do
expect('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF').to fully_match(subject)
end

it "must not match octets longer than four hex digits" do
expect('fffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff').to_not match(subject)
expect('ffff:fffff:ffff:ffff:ffff:ffff:ffff:ffff').to_not match(subject)
expect('ffff:ffff:fffff:ffff:ffff:ffff:ffff:ffff').to_not match(subject)
expect('ffff:ffff:ffff:fffff:ffff:ffff:ffff:ffff').to_not match(subject)
expect('ffff:ffff:ffff:ffff:fffff:ffff:ffff:ffff').to_not match(subject)
expect('ffff:ffff:ffff:ffff:ffff:fffff:ffff:ffff').to_not match(subject)
expect('ffff:ffff:ffff:ffff:ffff:ffff:fffff:ffff').to_not match(subject)
expect('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffff').to_not match(subject)
end

it "must match non-CIDR truncated IPv6 addresses" do
expect('::').to fully_match(subject)
expect('::2222:3333:4444:5555:6666:7777:8888').to fully_match(subject)
expect('1111::3333:4444:5555:6666:7777:8888').to fully_match(subject)
expect('1111:2222::4444:5555:6666:7777:8888').to fully_match(subject)
expect('1111:2222:3333::5555:6666:7777:8888').to fully_match(subject)
expect('1111:2222:3333:4444::6666:7777:8888').to fully_match(subject)
expect('1111:2222:3333:4444:5555::7777:8888').to fully_match(subject)
expect('1111:2222:3333:4444:5555:6666::8888').to fully_match(subject)
expect('1111:2222:3333:4444:5555:6666:7777::').to fully_match(subject)
end

it "must match non-CIDR IPv4-mapped IPv6 addresses" do
expect('::ffff:1.2.3.4').to fully_match(subject)
end

it "must match CIDR IPv6 addresses" do
expect('1111:2222:3333:4444:5555:6666:7777:8888/128').to fully_match(subject)
end

it "must match CIDR truncated IPv6 addresses" do
expect('::/128').to fully_match(subject)
expect('::2222:3333:4444:5555:6666:7777:8888/128').to fully_match(subject)
expect('1111::3333:4444:5555:6666:7777:8888/128').to fully_match(subject)
expect('1111:2222::4444:5555:6666:7777:8888/128').to fully_match(subject)
expect('1111:2222:3333::5555:6666:7777:8888/128').to fully_match(subject)
expect('1111:2222:3333:4444::6666:7777:8888/128').to fully_match(subject)
expect('1111:2222:3333:4444:5555::7777:8888/128').to fully_match(subject)
expect('1111:2222:3333:4444:5555:6666::8888/128').to fully_match(subject)
expect('1111:2222:3333:4444:5555:6666:7777::/128').to fully_match(subject)
end

it "must match CIDR IPv4-mapped IPv6 addresses" do
expect('::ffff:1.2.3.4/128').to fully_match(subject)
end

it "must not match bit lengths greater than 128" do
expect('1111:2222:3333:4444:5555:6666:7777:8888/129').to_not match(subject)
end
end

describe "#initialize" do
subject { described_class.new(cidr) }

it "must set #string" do
expect(subject.string).to eq(cidr)
end

context "when given an invalid IPv4 CIDR string" do
let(:cidr) { '256.256.256.256/32' }

it do
expect {
described_class.new(cidr)
}.to raise_error(ArgumentError,"invalid CIDR range: #{cidr.inspect}")
end
end

context "when given an invalid IPv6 CIDR string" do
let(:cidr) { '11:11:11:11:11:11/128' }

it do
expect {
described_class.new(cidr)
}.to raise_error(ArgumentError,"invalid CIDR range: #{cidr.inspect}")
end
end
end

describe ".parse" do
Expand Down

0 comments on commit 6b5fd7d

Please sign in to comment.