Skip to content

Commit 09d86eb

Browse files
committed
Add is_ip_range? method
1 parent 55396f6 commit 09d86eb

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

lib/rex/socket.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,29 @@ def self.dotted_ip?(addr)
173173
(support_ipv6? && self.is_ipv6?(addr)) || self.is_ipv4?(addr)
174174
end
175175

176+
def self.is_ip_range?(addr)
177+
elements = addr.split('-')
178+
return false if elements.length != 2
179+
180+
elements.map! { |e| e.strip }
181+
182+
# We were provided with a simple case of '1.1.1.1-1.2.3.4', or '::1-::ffff'
183+
return true if elements.all? { |element| is_ipv6?(element) } || elements.all? { |element| is_ipv4?(element) }
184+
185+
return false if elements.any? { |element| is_mac_addr?(element) }
186+
187+
# If we have a mix of IPv4 and IPv6 addresses, that's not a valid range
188+
return false if elements.any? { |element| is_ipv4?(element) } && elements.any? { |element| is_ipv6?(element) }
189+
190+
ipv4_ending_regex = /((.)?[0-9]{1,3})+$/.freeze
191+
return true if is_ipv4?(elements.first) && elements.last.match?(ipv4_ending_regex)
192+
193+
ipv6_ending_regex = /(^::[a-f0-9]{1,4})+$/.freeze
194+
return true if is_ipv6?(elements.first) && elements.last.downcase.match?(ipv6_ending_regex)
195+
196+
false
197+
end
198+
176199
# Checks to see if an address is an IPv6 address and if so, converts it into its
177200
# square bracket format for addressing as noted in RFC 6874 which states that an IPv6
178201
# address literal in a URL is always embedded between [ and ]. Please also refer to

spec/rex/socket_spec.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,13 @@
277277
end
278278
end
279279

280+
context 'with an IP CIDR' do
281+
let(:try) { '192.168.1.1/30' }
282+
it 'should return true' do
283+
expect(addr).to eq true
284+
end
285+
end
286+
280287
context 'with multiple IPv4 addresses' do
281288
context 'separated by newlines' do
282289
let(:try) { "127.0.0.1\n127.0.0.1" }
@@ -377,6 +384,20 @@
377384
end
378385
end
379386

387+
context 'with an ip address range' do
388+
let(:try) { '192.168.1.1-4' }
389+
it 'should return false' do
390+
expect(name).to eq false
391+
end
392+
end
393+
394+
context 'with an ip CIDR' do
395+
let(:try) { '192.168.1.1/24' }
396+
it 'should return false' do
397+
expect(name).to eq false
398+
end
399+
end
400+
380401
context 'with a hostname' do
381402
let(:try) { "localhost" }
382403
it "should return true" do
@@ -529,4 +550,48 @@
529550
end
530551
end
531552
end
553+
554+
describe '.is_ip_range?' do
555+
[
556+
{ input: '1.1.1.1 - 1.1.1.255', expected: true },
557+
{ input: '1.1.1.1- 1.1.1.255', expected: true },
558+
{ input: '1.1.1.1 -1.1.1.255', expected: true },
559+
{ input: '1.1.1.1-1.1.1.255', expected: true },
560+
{ input: ' 1.1.1.1 - 1.1.1.255 ', expected: true },
561+
{ input: '1.1.1.1 - 255', expected: true },
562+
{ input: '::1 - ::ffff', expected: true },
563+
{ input: '127.0.0.1-.5', expected: true },
564+
{ input: '127.0.0.1-5', expected: true },
565+
{ input: '127.0.0.1-1.5', expected: true },
566+
{ input: '127.0.0.1-.1.5', expected: true },
567+
{ input: '::1 - ::1', expected: true },
568+
{ input: '::ffff - ::ffff', expected: true },
569+
{ input: '1.1.1.1 - ::1', expected: false },
570+
{ input: '::1 - 1.1.1.1', expected: false },
571+
{ input: 'example-domain.com', expected: false },
572+
{ input: 'example-domain-name.com', expected: false },
573+
{ input: '127.0.0.1', expected: false },
574+
{ input: '::1', expected: false },
575+
{ input: '::ffff::ffff', expected: false },
576+
{ input: '::1-::ffffffff', expected: false },
577+
{ input: '::1-ffff', expected: false },
578+
{ input: '1-ffff', expected: false },
579+
{ input: '1-::ffff', expected: false },
580+
{ input: 'example-domain.com-example-subdomain.com', expected: false },
581+
{ input: 'example-domain.com-docs.rapid7.com', expected: false },
582+
{ input: 'example-domain.com-127.0.0.1', expected: false },
583+
{ input: '127.0.0.1-rapid7.com', expected: false },
584+
{ input: '1-rapid7.com', expected: false },
585+
{ input: '::1-rapid7.com::ffff', expected: false },
586+
{ input: '00:1A:2B:3C:4D:5E-00:A1:B2:C3:D4:E5', expected: false },
587+
{ input: '00:1A:2B:3C:4D:5E-00:A1:B2:C3:D4:E5', expected: false },
588+
].each do |test_case|
589+
it "should return #{test_case[:expected]} when given #{test_case[:input]}" do
590+
input = test_case[:input]
591+
expected = test_case[:expected]
592+
593+
expect(described_class.is_ip_range?(input)).to eq(expected)
594+
end
595+
end
596+
end
532597
end

0 commit comments

Comments
 (0)