# frozen_string_literal: true # # Copyright (c) 2006-2024 Hal Brodigan (postmodern.mod3 at gmail.com) # # ronin-support is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # ronin-support is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with ronin-support. If not, see . # require 'ronin/support/network/ip_range/cidr' require 'ronin/support/network/ip_range/glob' require 'ronin/support/network/ip_range/range' module Ronin module Support module Network # # Represents an IP range. # # ## Examples # # Enumerating over a CIDR range: # # IPRange.each('10.0.0.1/24') { |ip| puts ip } # # 10.0.0.0 # # 10.0.0.1 # # 10.0.0.2 # # ... # # 10.0.0.254 # # 10.0.0.255 # # Enumerating over a IP-glob range: # # IPRange.each('10.0.1-3.*') { |ip| puts ip } # # 10.0.1.0 # # 10.0.1.1 # # ... # # 10.0.1.254 # # 10.0.1.255 # # ... # # 10.0.2.0 # # 10.0.2.1 # # ... # # 10.0.2.254 # # 10.0.2.255 # # ... # # 10.0.3.0 # # 10.0.3.1 # # ... # # 10.0.3.254 # # 10.0.3.255 # # @api public # # @since 1.0.0 # class IPRange # Regular expression to match CIDR ranges or IP-glob ranges. # # @api private # # @since 1.1.0 REGEX = /#{CIDR::REGEX}|#{Glob::REGEX}/ # The parsed IP range. # # @return [CIDR, Glob] # # @api private # # @since 1.1.0 attr_reader :range # # Initializes the IP range. # # @param [String] string # The IP range string. # # @example Initializing a CIDR IP range: # ip_range = IPRange.new('10.0.0.1/24') # # @example Initializing an IP-glob range: # ip_range = IPRange.new('10.0.1-3.*') # # @raise [ArgumentError] # The IP range was neither a CIDR range or a IP-glob range. # def initialize(string) @range = case string when CIDR::REGEX then CIDR.new(string) when Glob::REGEX then Glob.new(string) else raise(ArgumentError,"invalid IP range: #{string.inspect}") end end # # Alias for {#initialize new}. # # @param [String] string # The IP range to parse. # # @return [IPRange] # The parsed IP range. # # @see #initialize # def self.parse(string) new(string) end # # Enumerates over each IP address that is included in the addresses # netmask. Supports both IPv4 and IPv6 addresses. # # @param [String] string # The IP range string to parse and enumerate over. # # @yield [ip] # The block which will be passed every IP address covered be the # netmask of the IPAddr object. # # @yieldparam [String] ip # An IP address. # # @return [Enumerator] # If no block is given an Enumerator will be returned. # # @example Enumerating over a CIDR range: # IPRange.each('10.0.0.1/24') { |ip| puts ip } # # 10.0.0.0 # # 10.0.0.1 # # 10.0.0.2 # # ... # # 10.0.0.254 # # 10.0.0.255 # # @example Enumerating over a IP-glob range: # IPRange.each('10.0.1-3.*') { |ip| puts ip } # # 10.0.1.0 # # 10.0.1.1 # # ... # # 10.0.1.254 # # 10.0.1.255 # # ... # # 10.0.2.0 # # 10.0.2.1 # # ... # # 10.0.2.254 # # 10.0.2.255 # # ... # # 10.0.3.0 # # 10.0.3.1 # # ... # # 10.0.3.254 # # 10.0.3.255 # # @see #each # def self.each(string,&block) new(string).each(&block) end # # Determines if the IP range is a CIDR range. # # @param [String] string # The IP range string to inspect. # # @return [Boolean] # Indicates that the IP range is a CIDR range. # def self.cidr?(string) string =~ CIDR::REGEX end # # Determines if the IP range is a IP-glob range. # # @param [String] string # The IP range string to inspect. # # @return [Boolean] # Indicates that the IP range is a IP-glob range. # def self.glob?(string) string =~ Glob::REGEX end # # The IP range string. # # @return [String] # # @see CIDR#string # @see Glob#string # def string @range.string end # # Determines if the IP range is IPv4. # # @return [Boolean] # # @see CIDR#ipv4? # @see Glob#ipv4? # def ipv4? @range.ipv4? end # # Determines if the IP range is IPv6. # # @return [Boolean] # # @see CIDR#ipv6? # @see Glob#ipv6? # def ipv6? @range.ipv6? end # # Determines whether the IP address exists within the IP range. # # @param [IP, IPAddr, String] ip # The IP address to check. # # @return [Boolean] # Indicates whether the IP address exists within the IP range. # # @since 1.1.0 # def include?(ip) @range.include?(ip) end # # Compares the IP range to another IP range. # # @param [Object] other # The other IP range. # # @return [Boolean] # # @since 1.1.0 # def ==(other) other.kind_of?(self.class) && @range == other.range end # # Determines if the given IP range is a sub-set of the IP range. # # @param [IPRange, Enumerable] other # The other IP range. # # @return [Boolean] # # @see CIDR#=== # @see Glob#=== # # @since 1.1.0 # def ===(other) case other when IPRange @range === other.range else @range === other end end # # Enumerates over each IP address that is included in the addresses # netmask. Supports both IPv4 and IPv6 addresses. # # @yield [ip] # The block which will be passed every IP address covered be the # netmask of the IPAddr object. # # @yieldparam [String] ip # An IP address. # # @return [Enumerator] # If no block is given an Enumerator will be returned. # # @example Enumerating over a CIDR range: # ip_range = IPRange.new('10.0.0.1/24') # ip_range.each { |ip| puts ip } # # 10.0.0.0 # # 10.0.0.1 # # 10.0.0.2 # # ... # # 10.0.0.254 # # 10.0.0.255 # # @example Enumerating over a IP-glob range: # ip_range = IPRange.new('10.0.1-3.*') # ip_range.each { |ip| puts ip } # # 10.0.1.0 # # 10.0.1.1 # # ... # # 10.0.1.254 # # 10.0.1.255 # # ... # # 10.0.2.0 # # 10.0.2.1 # # ... # # 10.0.2.254 # # 10.0.2.255 # # ... # # 10.0.3.0 # # 10.0.3.1 # # ... # # 10.0.3.254 # # 10.0.3.255 # # @see CIDR#each # @see Glob#each # def each(&block) @range.each(&block) end # # The first IP address in the IP range. # # @return [String] # # @see CIDR#first # @see Glob#last # # @since 1.1.0 # def first @range.first end # # The last IP address in the IP range. # # @return [String] # # @see CIDR#first # @see Glob#last # # @since 1.1.0 # def last @range.last end # # Calculates the size of the IP range. # # @return [Integer] # # @see CIDR#size # @see Glob#size # # @since 1.1.0 # def size @range.size end # # Converts the IP range back to a String. # # @return [String] # # @see CIDR#to_s # @see Glob#to_s # def to_s @range.to_s end # # Inspects the IP range. # # @return [String] # def inspect "#<#{self.class}: #{@range.string}>" end end end end end